diff --git a/src/App.tsx b/src/App.tsx index ba2329b..9bb5342 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,9 +34,9 @@ import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { Download } from './assets/svgs/Download.tsx'; -import { Logout } from './assets/svgs/Logout.tsx'; -import { Return } from './assets/svgs/Return.tsx'; +import { Download } from './assets/Icons/Download.tsx'; +import { Logout } from './assets/Icons/Logout.tsx'; +import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; import Success from './assets/svgs/Success.svg'; import './utils/seedPhrase/RandomSentenceGenerator'; @@ -69,13 +69,11 @@ import { Spacer } from './common/Spacer'; import { Loader } from './components/Loader'; import { PasswordField, ErrorText } from './components'; import { Group, requestQueueMemberNames } from './components/Group/Group'; -import { TaskManager } from './components/TaskManager/TaskManger'; +import { TaskManager } from './components/TaskManager/TaskManager.tsx'; import { useModal } from './common/useModal'; -import { Label } from './components/Group/AddGroup'; import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; import SettingsIcon from '@mui/icons-material/Settings'; import HelpIcon from '@mui/icons-material/Help'; - import { cleanUrl, getProtocol, @@ -119,11 +117,6 @@ import { } from './atoms/global'; import { useAppFullScreen } from './useAppFullscreen'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; -import { - openIndexedDB, - showSaveFilePicker, -} from './components/Apps/useQortalMessageListener'; -import { fileToBase64 } from './utils/fileReading'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { CoreSyncStatus } from './components/CoreSyncStatus'; import { Wallets } from './Wallets'; @@ -131,7 +124,6 @@ import { RandomSentenceGenerator } from './utils/seedPhrase/RandomSentenceGenera import { useFetchResources } from './common/useFetchResources'; import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './components/Tutorials/useHandleTutorials'; -import BoundedNumericTextField from './common/BoundedNumericTextField'; import { useHandleUserInfo } from './components/Group/useHandleUserInfo'; import { Minting } from './components/Minting/Minting'; import { isRunningGateway } from './qortalRequests'; @@ -139,7 +131,6 @@ import { QMailStatus } from './components/QMailStatus'; import { GlobalActions } from './components/GlobalActions/GlobalActions'; import { useBlockedAddresses } from './components/Group/useBlockUsers'; import { WalletIcon } from './assets/Icons/WalletIcon'; -import { DrawerUserLookup } from './components/Drawer/DrawerUserLookup'; import { UserLookup } from './components/UserLookup.tsx/UserLookup'; import { RegisterName } from './components/RegisterName'; import { BuyQortInformation } from './components/BuyQortInformation'; @@ -626,7 +617,9 @@ function App() { setIsDisabledEditorEnter(parsedVal); } } - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); useEffect(() => { @@ -684,7 +677,9 @@ function App() { try { if (typeof fileContents !== 'string') return; pf = JSON.parse(fileContents); - } catch (e) {} + } catch (e) { + console.log(error); + } try { const requiredFields = [ @@ -932,7 +927,9 @@ function App() { }); getBalanceFunc(); - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); useEffect(() => { @@ -985,7 +982,6 @@ function App() { ); } catch (error: any) { setWalletToBeDownloadedError(error?.message); - } finally { } }; @@ -1107,7 +1103,9 @@ function App() { error.message || 'An error occurred' ); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const returnToMain = () => { @@ -1333,9 +1331,7 @@ function App() { {authenticatedMode === 'qort' && ( + LITECOIN WALLET } @@ -1345,13 +1341,13 @@ function App() { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} @@ -1372,9 +1368,7 @@ function App() { {authenticatedMode === 'ltc' && ( + QORTAL WALLET } @@ -1384,13 +1378,13 @@ function App() { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} @@ -1435,10 +1429,10 @@ function App() { > {ltcBalance} LTC @@ -1447,7 +1441,6 @@ function App() { onClick={getLtcBalanceFunc} sx={{ fontSize: '16px', - color: 'white', cursor: 'pointer', }} /> @@ -1494,10 +1487,10 @@ function App() { > {balance?.toFixed(2)} QORT @@ -1506,7 +1499,6 @@ function App() { onClick={getBalanceFunc} sx={{ fontSize: '16px', - color: 'white', cursor: 'pointer', }} /> @@ -1517,13 +1509,13 @@ function App() { {userInfo && !userInfo?.name && ( { @@ -1548,12 +1540,12 @@ function App() { )} { @@ -1573,18 +1565,18 @@ function App() { return ( {isMobile && ( @@ -1610,10 +1601,10 @@ function App() { > @@ -1625,7 +1616,6 @@ function App() { title={ )} + + + { setIsOpenDrawerLookup(true); @@ -1713,7 +1705,6 @@ function App() { title={ + + { executeEvent('openWalletsApp', {}); @@ -1751,7 +1744,6 @@ function App() { title={ - - + + {extState === 'authenticated' && ( )} + {extState === 'authenticated' && isMainWindow && ( @@ -1873,7 +1865,9 @@ function App() { )} + + { try { @@ -1896,7 +1890,6 @@ function App() { title={ + {(desktopViewMode === 'apps' || desktopViewMode === 'home') && ( { @@ -1940,7 +1934,6 @@ function App() { title={ + BACKUP WALLET } @@ -1985,13 +1976,13 @@ function App() { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} @@ -2113,7 +2104,7 @@ function App() { width: '100%', height: '100%', position: 'fixed', - background: '#27282c', + background: theme.palette.background.default, display: 'flex', flexDirection: 'column', alignItems: 'center', @@ -2403,17 +2394,7 @@ function App() { )} {` ${requestBuyOrder?.crosschainAtInfo?.[0]?.foreignBlockchain}`} - {/* - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - /> */} + {requestConnection?.hostname}

requests authentication
+ + + + Authenticate + + { setExtstate('create-wallet'); @@ -2628,14 +2616,15 @@ function App() { {extState === 'wallets' && ( <> + + {rawWallet?.name ? rawWallet?.name : rawWallet?.address0} + + + <> Wallet Password + + + Authenticate + {walletToBeDecryptedError} @@ -2770,12 +2767,12 @@ function App() { + + + + {!walletToBeDownloaded && ( <> @@ -2858,14 +2859,15 @@ function App() { {!walletToBeDownloaded && ( <> + @@ -3007,11 +3008,11 @@ function App() { {generatorRef.current?.parsedString} @@ -3055,6 +3056,7 @@ function App() { + + Wallet Password + + + + Confirm Wallet Password + + + There is no minimum length requirement + @@ -3146,13 +3157,13 @@ function App() { {isOpenSendQortSuccess && ( @@ -3279,7 +3290,7 @@ function App() { + {logo?.name} + + - - - - ) -} + Trade QORT + +
+ + + + + Benefits of having QORT + + + + + + + + + + + + + + + +
+ + + + + + ); +}; diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index 6eaf541..5d89ad3 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -1,21 +1,7 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { GroupMail } from "../Group/Forum/GroupMail"; -import { MyContext, isMobile } from "../../App"; -import { getRootHeight } from "../../utils/mobile/mobileUtils"; -import { Box, Typography } from "@mui/material"; -import { AdminSpaceInner } from "./AdminSpaceInner"; - - - - - +import { useContext, useEffect, useState } from 'react'; +import { MyContext, isMobile } from '../../App'; +import { Box, Typography } from '@mui/material'; +import { AdminSpaceInner } from './AdminSpaceInner'; export const AdminSpace = ({ selectedGroup, @@ -26,11 +12,11 @@ export const AdminSpace = ({ isAdmin, myAddress, hide, - defaultThread, + defaultThread, setDefaultThread, - setIsForceShowCreationKeyPopup + setIsForceShowCreationKeyPopup, }) => { - const { rootHeight } = useContext(MyContext); + const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); useEffect(() => { if (hide) { @@ -42,26 +28,37 @@ export const AdminSpace = ({ return (
- {!isAdmin && Sorry, this space is only for Admins.} - {isAdmin && } - -
+ style={{ + // reference to change height + display: 'flex', + flexDirection: 'column', + height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)', + left: hide && '-1000px', + opacity: hide ? 0 : 1, + position: hide ? 'fixed' : 'relative', + visibility: hide && 'hidden', + width: '100%', + }} + > + {!isAdmin && ( + + Sorry, this space is only for Admins. + + )} + {isAdmin && ( + + )} + ); }; diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index f469e9e..bd6af61 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -1,39 +1,42 @@ -import React, { useCallback, useContext, useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from 'react'; import { MyContext, getArbitraryEndpointReact, getBaseApiReact, -} from "../../App"; -import { Box, Button, Typography } from "@mui/material"; +} from '../../App'; +import { Box, Button, Typography } from '@mui/material'; import { decryptResource, getPublishesFromAdmins, validateSecretKey, -} from "../Group/Group"; -import { getFee } from "../../background"; -import { base64ToUint8Array } from "../../qdn/encryption/group-encryption"; -import { uint8ArrayToObject } from "../../backgroundFunctions/encryption"; -import { formatTimestampForum } from "../../utils/time"; -import { Spacer } from "../../common/Spacer"; +} from '../Group/Group'; +import { getFee } from '../../background'; +import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; +import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { formatTimestampForum } from '../../utils/time'; +import { Spacer } from '../../common/Spacer'; export const getPublishesFromAdminsAdminSpace = async ( admins: string[], groupId ) => { - const queryString = admins.map((name) => `name=${name}`).join("&"); + const queryString = admins.map((name) => `name=${name}`).join('&'); const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=admins-symmetric-qchat-group-${groupId}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; const response = await fetch(url); + if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const adminData = await response.json(); const filterId = adminData.filter( (data: any) => data.identifier === `admins-symmetric-qchat-group-${groupId}` ); + if (filterId?.length === 0) { return false; } + const sortedData = filterId.sort((a: any, b: any) => { // Get the most recent date for both a and b const dateA = a.updated ? new Date(a.updated) : new Date(a.created); @@ -87,10 +90,11 @@ export const AdminSpaceInner = ({ const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); + throw new Error('SecretKey is not valid'); setAdminGroupSecretKey(decryptedKeyToObject); setAdminGroupSecretKeyPublishDetails(getLatestPublish); } catch (error) { + console.log(error); } finally { setIsFetchingAdminGroupSecretKey(false); } @@ -106,6 +110,7 @@ export const AdminSpaceInner = ({ if (getLatestPublish === false) setGroupSecretKeyPublishDetails(false); setGroupSecretKeyPublishDetails(getLatestPublish); } catch (error) { + console.log(error); } finally { setIsFetchingGroupSecretKey(false); } @@ -113,15 +118,17 @@ export const AdminSpaceInner = ({ const createCommonSecretForAdmins = async () => { try { - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); + await show({ - message: "Would you like to perform an ARBITRARY transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform an ARBITRARY transaction?', + publishFee: fee.fee + ' QORT', }); + setIsLoadingPublishKey(true); window - .sendMessage("encryptAndPublishSymmetricKeyGroupChatForAdmins", { + .sendMessage('encryptAndPublishSymmetricKeyGroupChatForAdmins', { groupId: selectedGroup, previousData: adminGroupSecretKey, admins: adminsWithNames, @@ -129,27 +136,29 @@ export const AdminSpaceInner = ({ .then((response) => { if (!response?.error) { setInfoSnackCustom({ - type: "success", + type: 'success', message: - "Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.", + 'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.', }); setOpenSnackGlobal(true); return; } setInfoSnackCustom({ - type: "error", - message: response?.error || "unable to re-encrypt secret key", + type: 'error', + message: response?.error || 'unable to re-encrypt secret key', }); setOpenSnackGlobal(true); }) .catch((error) => { setInfoSnackCustom({ - type: "error", - message: error?.message || "unable to re-encrypt secret key", + type: 'error', + message: error?.message || 'unable to re-encrypt secret key', }); setOpenSnackGlobal(true); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; useEffect(() => { @@ -159,27 +168,32 @@ export const AdminSpaceInner = ({ return ( - Reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait. + + Reminder: After publishing the key, it will take a couple of minutes for + it to appear. Please just wait. + {isFetchingGroupSecretKey && ( @@ -191,33 +205,47 @@ export const AdminSpaceInner = ({ )} {groupSecretKeyPublishDetails && ( - Last encryption date:{" "} + Last encryption date:{' '} {formatTimestampForum( groupSecretKeyPublishDetails?.updated || groupSecretKeyPublishDetails?.created - )}{" "} + )}{' '} {` by ${groupSecretKeyPublishDetails?.name}`} )} - + - This key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key. + + + This key is to encrypt GROUP related content. This is the only one + used in this UI as of now. All group members will be able to see + content encrypted with this key. + + + {isFetchingAdminGroupSecretKey && ( @@ -228,20 +256,31 @@ export const AdminSpaceInner = ({ )} {adminGroupSecretKeyPublishDetails && ( - Last encryption date:{" "} + Last encryption date:{' '} {formatTimestampForum( adminGroupSecretKeyPublishDetails?.updated || adminGroupSecretKeyPublishDetails?.created )} )} - + - This key is to encrypt ADMIN related content. Only admins would see content encrypted with it. + + + This key is to encrypt ADMIN related content. Only admins would see + content encrypted with it. + ); diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 5ebe885..e3c3976 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -4,7 +4,7 @@ import { AuthenticatedContainerInnerTop, CustomButton, } from '../../styles/App-styles'; -import { Box, CircularProgress } from '@mui/material'; +import { Box, CircularProgress, useTheme } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import ShortUniqueId from 'short-unique-id'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; @@ -39,6 +39,7 @@ export const AnnouncementDiscussion = ({ myName, isPrivate, }) => { + const theme = useTheme(); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); @@ -83,7 +84,9 @@ export const AnnouncementDiscussion = ({ [`${identifier}-${name}`]: messageData, }; }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnc = async ({ encryptedData, identifier }: any) => { @@ -107,7 +110,9 @@ export const AnnouncementDiscussion = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const setTempData = async () => { @@ -123,7 +128,9 @@ export const AnnouncementDiscussion = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishComment = async () => { @@ -206,6 +213,7 @@ export const AnnouncementDiscussion = ({ getData({ name: data.name, identifier: data.identifier }, isPrivate); } } catch (error) { + console.log(error); } finally { setIsLoading(false); @@ -235,7 +243,9 @@ export const AnnouncementDiscussion = ({ for (const data of responseData) { getData({ name: data.name, identifier: data.identifier }, isPrivate); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const combinedListTempAndReal = useMemo(() => { @@ -266,19 +276,19 @@ export const AnnouncementDiscussion = ({ return (
+
@@ -345,11 +353,11 @@ export const AnnouncementDiscussion = ({ {isFocusedParent && ( @@ -361,13 +369,13 @@ export const AnnouncementDiscussion = ({ // Unfocus the editor }} style={{ - marginTop: 'auto', alignSelf: 'center', + background: 'red', cursor: isSending ? 'default' : 'pointer', flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', - background: 'red', + fontSize: '14px', + marginTop: 'auto', + padding: '5px', }} > {` Close`} @@ -379,25 +387,25 @@ export const AnnouncementDiscussion = ({ publishComment(); }} style={{ - marginTop: 'auto', alignSelf: 'center', + background: theme.palette.background.default, cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', + fontSize: '14px', + marginTop: 'auto', + padding: '5px', }} > {isSending && ( )} diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index 758a451..73faf24 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -1,173 +1,206 @@ -import { Message } from "@chatscope/chat-ui-kit-react"; -import React, { useEffect, useState } from "react"; -import { useInView } from "react-intersection-observer"; -import { MessageDisplay } from "./MessageDisplay"; -import { Avatar, Box, Typography } from "@mui/material"; -import { formatTimestamp } from "../../utils/time"; +import React, { useEffect, useState } from 'react'; +import { MessageDisplay } from './MessageDisplay'; +import { Avatar, Box, Typography, useTheme } from '@mui/material'; +import { formatTimestamp } from '../../utils/time'; import ChatBubbleIcon from '@mui/icons-material/ChatBubble'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; -import { getBaseApi } from "../../background"; -import { requestQueueCommentCount } from "./GroupAnnouncements"; -import { CustomLoader } from "../../common/CustomLoader"; -import { getArbitraryEndpointReact, getBaseApiReact } from "../../App"; -import { WrapperUserAction } from "../WrapperUserAction"; -export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement, disableComment, myName }) => { +import { getBaseApi } from '../../background'; +import { requestQueueCommentCount } from './GroupAnnouncements'; +import { CustomLoader } from '../../common/CustomLoader'; +import { getArbitraryEndpointReact, getBaseApiReact } from '../../App'; +import { WrapperUserAction } from '../WrapperUserAction'; - const [commentLength, setCommentLength] = useState(0) - const getNumberOfComments = React.useCallback( - async () => { - try { - const offset = 0; +export const AnnouncementItem = ({ + message, + messageData, + setSelectedAnnouncement, + disableComment, + myName, +}) => { + const theme = useTheme(); + const [commentLength, setCommentLength] = useState(0); + const getNumberOfComments = React.useCallback(async () => { + try { + const offset = 0; - // dispatch(setIsLoadingGlobal(true)) - const identifier = `cm-${message.identifier}`; - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=0&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; - - const response = await requestQueueCommentCount.enqueue(() => { - return fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - }) - const responseData = await response.json(); + // dispatch(setIsLoadingGlobal(true)) + const identifier = `cm-${message.identifier}`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=0&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; + + const response = await requestQueueCommentCount.enqueue(() => { + return fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + const responseData = await response.json(); + + setCommentLength(responseData?.length); + } catch (error) { + console.log(error); + } + }, []); + + useEffect(() => { + if (disableComment) return; + getNumberOfComments(); + }, []); - setCommentLength(responseData?.length); - - } catch (error) { - } finally { - // dispatch(setIsLoadingGlobal(false)) - } - }, - [] - ); - useEffect(()=> { - if(disableComment) return - getNumberOfComments() - }, []) return (
- - - - {message?.name?.charAt(0)} - - - - + + {message?.name?.charAt(0)} + + + - {message?.name} - - - {!messageData?.decryptedData && ( - - - - )} - {messageData?.decryptedData?.message && ( - <> - {messageData?.type === "notification" ? ( - - ) : ( - - )} - - )} + + + {message?.name} + + + {!messageData?.decryptedData && ( + + + + )} + {messageData?.decryptedData?.message && ( + <> + {messageData?.type === 'notification' ? ( + + ) : ( + + )} + + )} - - - {formatTimestamp(message.created)} + + + {formatTimestamp(message.created)} + + - - {!disableComment && ( - setSelectedAnnouncement(message)}> - - - - {commentLength ? ( - {`${commentLength > 1 ? `${commentLength} comments` : `${commentLength} comment`}`} - ) : ( - Leave comment - )} - + {!disableComment && ( + setSelectedAnnouncement(message)} + > + + + {commentLength ? ( + {`${commentLength > 1 ? `${commentLength} comments` : `${commentLength} comment`}`} + ) : ( + + Leave comment + + )} + + - - - )} - + )}
); }; diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index efaf1fa..9c32cc1 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -1,10 +1,5 @@ -import React, { useCallback, useState, useEffect, useRef } from 'react'; -import { - List, - AutoSizer, - CellMeasurerCache, - CellMeasurer, -} from 'react-virtualized'; +import { useState, useEffect, useRef } from 'react'; +import { CellMeasurerCache } from 'react-virtualized'; import { AnnouncementItem } from './AnnouncementItem'; import { Box } from '@mui/material'; import { CustomButton } from '../../styles/App-styles'; @@ -37,12 +32,12 @@ export const AnnouncementList = ({ return (
@@ -57,11 +52,11 @@ export const AnnouncementList = ({
*/} {showLoadMore && ( diff --git a/src/components/Chat/ChatContainer.tsx b/src/components/Chat/ChatContainer.tsx index 399e5a4..2823b83 100644 --- a/src/components/Chat/ChatContainer.tsx +++ b/src/components/Chat/ChatContainer.tsx @@ -1,16 +1,14 @@ -import React, { useState } from "react"; -import InfiniteScroll from "react-infinite-scroller"; import { MainContainer, ChatContainer, MessageList, Message, MessageInput, - Avatar -} from "@chatscope/chat-ui-kit-react"; -import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; + Avatar, +} from '@chatscope/chat-ui-kit-react'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; -export const ChatContainerComp = ({messages}) => { +export const ChatContainerComp = ({ messages }) => { // const [messages, setMessages] = useState([ // { id: 1, text: "Hello! How are you?", sender: "Joe"}, // { id: 2, text: "I'm good, thank you!", sender: "Me" } @@ -26,31 +24,31 @@ export const ChatContainerComp = ({messages}) => { // }; return ( -
+
- - {messages.map((msg) => ( - - {msg.direction === "incoming" && } - - ))} - - + + {messages.map((msg) => ( + + {msg.direction === 'incoming' && ( + + )} + + ))} + +
); }; - - diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 1af3f8e..49d7f94 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -7,13 +7,12 @@ import React, { useState, } from 'react'; -import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { ChatList } from './ChatList'; import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; import Tiptap from './TipTap'; import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; -import { Box, ButtonBase, Input, Typography } from '@mui/material'; +import { Box, ButtonBase, Input, Typography, useTheme } from '@mui/material'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; @@ -36,7 +35,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ShortUniqueId from 'short-unique-id'; import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; import { ExitIcon } from '../../assets/Icons/ExitIcon'; -import { MessageItem, ReplyPreview } from './MessageItem'; +import { ReplyPreview } from './MessageItem'; const uid = new ShortUniqueId({ length: 5 }); @@ -52,6 +51,7 @@ export const ChatDirect = ({ close, setMobileViewModeKeepOpen, }) => { + const theme = useTheme(); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [onEditMessage, setOnEditMessage] = useState(null); @@ -87,7 +87,9 @@ export const ChatDirect = ({ const publicKey = await getPublicKey(address); if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return; setPublicKeyOfRecipient(publicKey); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const tempMessages = useMemo(() => { @@ -167,6 +169,7 @@ export const ChatDirect = ({ text: item.message, unread: item?.sender === myAddress ? false : true, })); + setMessages((prev) => [...prev, ...formatted]); setChatReferences((prev) => { const organizedChatReferences = { ...prev }; @@ -183,7 +186,9 @@ export const ChatDirect = ({ {}), edit: item, }; - } catch (error) {} + } catch (error) { + console.log(error); + } }); return organizedChatReferences; }); @@ -214,7 +219,9 @@ export const ChatDirect = ({ {}), edit: item, }; - } catch (error) {} + } catch (error) { + console.log(error); + } }); return organizedChatReferences; }); @@ -227,7 +234,9 @@ export const ChatDirect = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const forceCloseWebSocket = () => { @@ -368,7 +377,6 @@ export const ChatDirect = ({ senderName: myName, }); setNewChat(null); - window .sendMessage('addTimestampEnterChat', { timestamp: Date.now(), @@ -396,7 +404,6 @@ export const ChatDirect = ({ }); } catch (error) { throw new Error(error); - } finally { } }; const clearEditorContent = () => { @@ -537,39 +544,39 @@ export const ChatDirect = ({ return (
{!isMobile && ( Close Direct Chat @@ -579,26 +586,26 @@ export const ChatDirect = ({ {isMobile && ( @@ -623,10 +630,10 @@ export const ChatDirect = ({
{replyMessage && ( @@ -723,9 +728,9 @@ export const ChatDirect = ({ {onEditMessage && ( @@ -735,7 +740,6 @@ export const ChatDirect = ({ onClick={() => { setReplyMessage(null); setOnEditMessage(null); - clearEditorContent(); }} > @@ -756,9 +760,9 @@ export const ChatDirect = ({ {isSending && ( )} @@ -815,12 +820,14 @@ export const ChatDirect = ({
+ + { @@ -192,7 +189,9 @@ export const ChatGroup = ({ handleSecretKeyCreationInProgress(); return; } - } catch (error) {} + } catch (error) { + console.log(error); + } }); }; @@ -578,7 +577,9 @@ export const ChatGroup = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const forceCloseWebSocket = () => { @@ -621,7 +622,9 @@ export const ChatGroup = ({ middletierFunc(JSON.parse(e.data), selectedGroup); setIsLoading(false); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; socketRef.current.onclose = () => { clearTimeout(groupSocketTimeoutRef.current); @@ -700,7 +703,9 @@ export const ChatGroup = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const sendChatGroup = async ({ @@ -991,16 +996,18 @@ export const ChatGroup = ({ setIsOpenQManager(true); }, []); + const theme = useTheme(); + return (
{replyMessage && ( @@ -1078,9 +1083,9 @@ export const ChatGroup = ({ {onEditMessage && ( @@ -1113,9 +1118,9 @@ export const ChatGroup = ({ {isSending && ( )} @@ -1175,25 +1182,24 @@ export const ChatGroup = ({ {isOpenQManager !== null && ( Q-Manager @@ -1241,12 +1246,14 @@ export const ChatGroup = ({ )} {/* */} + + { const parentRef = useRef(); const [messages, setMessages] = useState(initialMessages); @@ -42,33 +34,32 @@ export const ChatList = ({ // Initialize the virtualizer const rowVirtualizer = useVirtualizer({ count: messages.length, - getItemKey: (index) => messages[index]?.tempSignature || messages[index].signature, + getItemKey: (index) => + messages[index]?.tempSignature || messages[index].signature, getScrollElement: () => parentRef?.current, estimateSize: useCallback(() => 80, []), // Provide an estimated height of items, adjust this as needed overscan: 10, // Number of items to render outside the visible area to improve smoothness }); - const isAtBottom = useMemo(()=> { + const isAtBottom = useMemo(() => { if (parentRef.current && rowVirtualizer?.isScrolling !== undefined) { const { scrollTop, scrollHeight, clientHeight } = parentRef.current; - const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed - return atBottom - } + const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed + return atBottom; + } - return false - - }, [rowVirtualizer?.isScrolling]) + return false; + }, [rowVirtualizer?.isScrolling]); useEffect(() => { if (!parentRef.current || rowVirtualizer?.isScrolling === undefined) return; - if(isAtBottom){ + if (isAtBottom) { if (scrollingIntervalRef.current) { clearTimeout(scrollingIntervalRef.current); } setShowScrollDownButton(false); return; - } else - if (rowVirtualizer?.isScrolling) { + } else if (rowVirtualizer?.isScrolling) { if (scrollingIntervalRef.current) { clearTimeout(scrollingIntervalRef.current); } @@ -108,7 +99,13 @@ export const ChatList = ({ setTimeout(() => { const hasUnreadMessages = totalMessages.some( - (msg) => msg.unread && !msg?.chatReference && !msg?.isTemp && (!msg?.chatReference && msg?.timestamp > lastSeenUnreadMessageTimestamp.current || 0) + (msg) => + msg.unread && + !msg?.chatReference && + !msg?.isTemp && + ((!msg?.chatReference && + msg?.timestamp > lastSeenUnreadMessageTimestamp.current) || + 0) ); if (parentRef.current) { const { scrollTop, scrollHeight, clientHeight } = parentRef.current; @@ -136,9 +133,9 @@ export const ChatList = ({ const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1; if (rowVirtualizer) { if (divideIndex) { - rowVirtualizer.scrollToIndex(divideIndex, { align: "start" }); + rowVirtualizer.scrollToIndex(divideIndex, { align: 'start' }); } else { - rowVirtualizer.scrollToIndex(index, { align: "end" }); + rowVirtualizer.scrollToIndex(index, { align: 'end' }); } } handleMessageSeen(); @@ -152,7 +149,7 @@ export const ChatList = ({ })) ); setShowScrollButton(false); - lastSeenUnreadMessageTimestamp.current = Date.now() + lastSeenUnreadMessageTimestamp.current = Date.now(); }, []); const sentNewMessageGroupFunc = useCallback(() => { @@ -166,9 +163,9 @@ export const ChatList = ({ }, [messages]); useEffect(() => { - subscribeToEvent("sent-new-message-group", sentNewMessageGroupFunc); + subscribeToEvent('sent-new-message-group', sentNewMessageGroupFunc); return () => { - unsubscribeFromEvent("sent-new-message-group", sentNewMessageGroupFunc); + unsubscribeFromEvent('sent-new-message-group', sentNewMessageGroupFunc); }; }, [sentNewMessageGroupFunc]); @@ -181,21 +178,24 @@ export const ChatList = ({ const goToMessage = useCallback((idx) => { rowVirtualizer.scrollToIndex(idx); }, []); + + const theme = useTheme(); + return (
- {rowVirtualizer.getVirtualItems().map((virtualRow) => { const index = virtualRow.index; let message = messages[index] || null; // Safeguard against undefined @@ -231,7 +230,7 @@ export const ChatList = ({ let reply = null; let reactions = null; let isUpdating = false; - + try { // Safeguard for message existence if (message) { @@ -239,16 +238,19 @@ export const ChatList = ({ replyIndex = messages.findIndex( (msg) => msg?.signature === message?.repliedTo ); - + if (message?.repliedTo && replyIndex !== -1) { reply = { ...(messages[replyIndex] || {}) }; if (chatReferences?.[reply?.signature]?.edit) { - reply.decryptedData = chatReferences[reply?.signature]?.edit; - reply.text = chatReferences[reply?.signature]?.edit?.message; - reply.editTimestamp = chatReferences[reply?.signature]?.edit?.timestamp + reply.decryptedData = + chatReferences[reply?.signature]?.edit; + reply.text = + chatReferences[reply?.signature]?.edit?.message; + reply.editTimestamp = + chatReferences[reply?.signature]?.edit?.timestamp; } } - + // GroupDirectId logic if (message?.message && message?.groupDirectId) { replyIndex = messages.findIndex( @@ -264,24 +266,34 @@ export const ChatList = ({ status: message?.status, }; } - + // Check for reactions and edits if (chatReferences?.[message.signature]) { - reactions = chatReferences[message.signature]?.reactions || null; - - if (chatReferences[message.signature]?.edit?.message && message?.text) { - message.text = chatReferences[message.signature]?.edit?.message; - message.isEdit = true - message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp + reactions = + chatReferences[message.signature]?.reactions || null; + + if ( + chatReferences[message.signature]?.edit?.message && + message?.text + ) { + message.text = + chatReferences[message.signature]?.edit?.message; + message.isEdit = true; + message.editTimestamp = + chatReferences[message.signature]?.edit?.timestamp; } - if (chatReferences[message.signature]?.edit?.messageText && message?.messageText) { - message.messageText = chatReferences[message.signature]?.edit?.messageText; - message.isEdit = true - message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp + if ( + chatReferences[message.signature]?.edit?.messageText && + message?.messageText + ) { + message.messageText = + chatReferences[message.signature]?.edit?.messageText; + message.isEdit = true; + message.editTimestamp = + chatReferences[message.signature]?.edit?.timestamp; } - } - + // Check if message is updating if ( tempChatReferences?.some( @@ -292,34 +304,37 @@ export const ChatList = ({ } } } catch (error) { - console.error("Error processing message:", error, { index, message }); + console.error('Error processing message:', error, { + index, + message, + }); // Gracefully handle the error by providing fallback data message = null; reply = null; reactions = null; } - // Render fallback if message is null + // Render fallback if message is null if (!message) { - return ( -
- Error loading message. -
- ); - } + return ( +
+ Error loading message. +
+ ); + } return (
- Error loading content: Invalid Data } > - - + +
); })} -
@@ -377,17 +390,17 @@ export const ChatList = ({
{enableMentions && (hasSecretKey || isPrivate === false) && ( { return convert(htmlString, { wordwrap: false, // Disable word wrapping })?.toLowerCase(); }; + const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, }); -export const ChatOptions = ({ messages : untransformedMessages, goToMessage, members, myName, selectedGroup, openQManager, isPrivate }) => { - const [mode, setMode] = useState("default"); - const [searchValue, setSearchValue] = useState(""); +export const ChatOptions = ({ + messages: untransformedMessages, + goToMessage, + members, + myName, + selectedGroup, + openQManager, + isPrivate, +}) => { + const [mode, setMode] = useState('default'); + const [searchValue, setSearchValue] = useState(''); const [selectedMember, setSelectedMember] = useState(0); - + const theme = useTheme(); const parentRef = useRef(); const parentRefMentions = useRef(); - const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null) - const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value - const messages = useMemo(()=> { - return untransformedMessages?.map((item)=> { - if(item?.messageText){ - let transformedMessage = item?.messageText + const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null); + const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value + const messages = useMemo(() => { + return untransformedMessages?.map((item) => { + if (item?.messageText) { + let transformedMessage = item?.messageText; try { - transformedMessage = generateHTML(item?.messageText, [ - StarterKit, - Underline, - Highlight, - Mention - ]) - return { - ...item, - messageText: transformedMessage - } + transformedMessage = generateHTML(item?.messageText, [ + StarterKit, + Underline, + Highlight, + Mention, + ]); + return { + ...item, + messageText: transformedMessage, + }; } catch (error) { // error } - } else return item - }) - }, [untransformedMessages]) + } else return item; + }); + }, [untransformedMessages]); + const getTimestampMention = async () => { try { return new Promise((res, rej) => { - window.sendMessage("getTimestampMention") - .then((response) => { - if (!response?.error) { - if(response && selectedGroup && response[selectedGroup]){ - setLastMentionTimestamp(response[selectedGroup]) - - - - - - } - - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + window + .sendMessage('getTimestampMention') + .then((response) => { + if (!response?.error) { + if (response && selectedGroup && response[selectedGroup]) { + setLastMentionTimestamp(response[selectedGroup]); + } + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; - useEffect(()=> { - if(mode === 'mentions' && selectedGroup){ - window.sendMessage("addTimestampMention", { - timestamp: Date.now(), - groupId: selectedGroup - }).then((res)=> { - getTimestampMention() - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + useEffect(() => { + if (mode === 'mentions' && selectedGroup) { + window + .sendMessage('addTimestampMention', { + timestamp: Date.now(), + groupId: selectedGroup, + }) + .then((res) => { + getTimestampMention(); + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); } - }, [mode, selectedGroup]) + }, [mode, selectedGroup]); - useEffect(()=> { - getTimestampMention() - }, []) + useEffect(() => { + getTimestampMention(); + }, []); // Debounce logic useEffect(() => { @@ -151,32 +159,39 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem .filter( (message) => message?.senderName === selectedMember && - extractTextFromHTML(isPrivate ? message?.messageText : message?.decryptedData?.message)?.includes( - debouncedValue.toLowerCase() - ) + extractTextFromHTML( + isPrivate ? message?.messageText : message?.decryptedData?.message + )?.includes(debouncedValue.toLowerCase()) ) ?.sort((a, b) => b?.timestamp - a?.timestamp); } return messages .filter((message) => - extractTextFromHTML(isPrivate === false ? message?.messageText : message?.decryptedData?.message)?.includes(debouncedValue.toLowerCase()) + extractTextFromHTML( + isPrivate === false + ? message?.messageText + : message?.decryptedData?.message + )?.includes(debouncedValue.toLowerCase()) ) ?.sort((a, b) => b?.timestamp - a?.timestamp); }, [debouncedValue, messages, selectedMember, isPrivate]); const mentionList = useMemo(() => { - if(!messages || messages.length === 0 || !myName) return [] - if(isPrivate === false){ + if (!messages || messages.length === 0 || !myName) return []; + if (isPrivate === false) { return messages - .filter((message) => - extractTextFromHTML(message?.messageText)?.includes(`@${myName?.toLowerCase()}`) - ) - ?.sort((a, b) => b?.timestamp - a?.timestamp); - + .filter((message) => + extractTextFromHTML(message?.messageText)?.includes( + `@${myName?.toLowerCase()}` + ) + ) + ?.sort((a, b) => b?.timestamp - a?.timestamp); } return messages .filter((message) => - extractTextFromHTML(message?.decryptedData?.message)?.includes(`@${myName?.toLowerCase()}`) + extractTextFromHTML(message?.decryptedData?.message)?.includes( + `@${myName?.toLowerCase()}` + ) ) ?.sort((a, b) => b?.timestamp - a?.timestamp); }, [messages, myName, isPrivate]); @@ -203,60 +218,54 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem overscan: 10, // Number of items to render outside the visible area to improve smoothness }); - - - if (mode === "mentions") { + if (mode === 'mentions') { return ( { - setMode("default"); + setMode('default'); }} sx={{ - cursor: "pointer", - color: "white", + cursor: 'pointer', + color: theme.palette.text.primary, }} /> - - - {mentionList?.length === 0 && ( No results @@ -264,72 +273,77 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem )}
- {rowVirtualizerMentions.getVirtualItems().map((virtualRow) => { - const index = virtualRow.index; - let message = mentionList[index]; - return ( -
- - -
- ); - })} + {rowVirtualizerMentions + .getVirtualItems() + .map((virtualRow) => { + const index = virtualRow.index; + let message = mentionList[index]; + return ( +
+ +
+ ); + })}
@@ -340,47 +354,46 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem ); } - if (mode === "search") { + if (mode === 'search') { return ( { - setMode("default"); + setMode('default'); }} sx={{ - cursor: "pointer", - color: "white", + cursor: 'pointer', + color: theme.palette.text.primary, }} /> @@ -392,8 +405,8 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem sx={{ ml: 1, flex: 1 }} placeholder="Search chat text" inputProps={{ - "aria-label": "Search for apps", - fontSize: "16px", + 'aria-label': 'Search for apps', + fontSize: '16px', fontWeight: 400, }} /> @@ -402,7 +415,7 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem {searchValue && ( { - setSearchValue(""); + setSearchValue(''); }} > @@ -411,52 +424,51 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem - - {!!selectedMember && ( - { - setSelectedMember(0); - }} sx={{ - cursor: "pointer", - color: "white", + alignItems: 'center', + display: 'flex', + justifyContent: 'space-between', + padding: '10px', }} - /> - )} - - - + > + + {!!selectedMember && ( + { + setSelectedMember(0); + }} + sx={{ + cursor: 'pointer', + color: theme.palette.text.primary, + }} + /> + )} + + {debouncedValue && searchedList?.length === 0 && ( No results @@ -464,79 +476,81 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem )}
{rowVirtualizer.getVirtualItems().map((virtualRow) => { const index = virtualRow.index; let message = searchedList[index]; return ( -
- - Error loading content: Invalid Data - - } - > - + + Error loading content: Invalid Data + + } + > +
- ); })}
@@ -551,46 +565,58 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem return ( - { - setMode("search") - }}> + { + setMode('search'); + }} + > SEARCH} + title={ + + SEARCH + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: "#444444", + color: theme.palette.text.secondary, }, }, }} @@ -598,157 +624,188 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem - { - setMode("default") - setSearchValue('') - setSelectedMember(0) - openQManager() - }}> + { + setMode('default'); + setSearchValue(''); + setSelectedMember(0); + openQManager(); + }} + > Q-MANAGER} + title={ + + Q-MANAGER + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: "#444444", + color: theme.palette.text.secondary, }, }, }} > - + - - { - setMode("mentions") - setSearchValue('') - setSelectedMember(0) - }}> - MENTIONED} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, + + { + setMode('mentions'); + setSearchValue(''); + setSelectedMember(0); }} > - 0 && (!lastMentionTimestamp || lastMentionTimestamp < mentionList[0]?.timestamp) ? 'var(--unread)' : 'white' - }} /> - - - + + MENTIONED + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.secondary, + }, + }, + }} + > + 0 && + (!lastMentionTimestamp || + lastMentionTimestamp < mentionList[0]?.timestamp) + ? 'var(--unread)' + : 'white', + }} + /> + + - - ); }; - -const ShowMessage = ({message, goToMessage, messages})=> { +const ShowMessage = ({ message, goToMessage, messages }) => { + const theme = useTheme(); return ( - - - - - {message?.senderName?.charAt(0)} - - - {message?.senderName} - - - - - {formatTimestamp(message.timestamp)} - { - const findMsgIndex = messages.findIndex( - (item) => - item?.signature === message?.signature - ); - if (findMsgIndex !== -1) { - goToMessage(findMsgIndex); - } - }} - > - {message?.messageText && ( - - )} - {message?.decryptedData?.message && ( -

" - } - /> - )} - -
-
- ) -} \ No newline at end of file + + + + + {message?.senderName?.charAt(0)} + + + {message?.senderName} + + + + + + + + {formatTimestamp(message.timestamp)} + + { + const findMsgIndex = messages.findIndex( + (item) => item?.signature === message?.signature + ); + if (findMsgIndex !== -1) { + goToMessage(findMsgIndex); + } + }} + > + {message?.messageText && ( + + )} + {message?.decryptedData?.message && ( +

'} + /> + )} +
+
+ ); +}; diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index 4c9f1d4..6c4fcfa 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -1,35 +1,56 @@ -import { Box, Button, Typography } from '@mui/material' -import React, { useContext } from 'react' +import React, { useContext } from 'react'; +import { Box, Button, Typography, useTheme } from '@mui/material'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { LoadingButton } from '@mui/lab'; -import { MyContext, getArbitraryEndpointReact, getBaseApiReact, pauseAllQueues } from '../../App'; +import { + MyContext, + getArbitraryEndpointReact, + getBaseApiReact, + pauseAllQueues, +} from '../../App'; import { getFee } from '../../background'; -import { decryptResource, getGroupAdmins, validateSecretKey } from '../Group/Group'; +import { + decryptResource, + getGroupAdmins, + validateSecretKey, +} from '../Group/Group'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; -export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, secretKeyDetails, userInfo, noSecretKey, setHideCommonKeyPopup, setIsForceShowCreationKeyPopup, isForceShowCreationKeyPopup}) => { +export const CreateCommonSecret = ({ + groupId, + secretKey, + isOwner, + myAddress, + secretKeyDetails, + userInfo, + noSecretKey, + setHideCommonKeyPopup, + setIsForceShowCreationKeyPopup, + isForceShowCreationKeyPopup, +}) => { const { show, setTxList } = useContext(MyContext); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); - const [isLoading, setIsLoading] = React.useState(false) + const [isLoading, setIsLoading] = React.useState(false); + + const theme = useTheme(); const getPublishesFromAdmins = async (admins: string[]) => { // const validApi = await findUsableApi(); - const queryString = admins.map((name) => `name=${name}`).join("&"); + const queryString = admins.map((name) => `name=${name}`).join('&'); const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${ groupId }&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; const response = await fetch(url); - if(!response.ok){ - throw new Error('network error') + if (!response.ok) { + throw new Error('network error'); } const adminData = await response.json(); - + const filterId = adminData.filter( - (data: any) => - data.identifier === `symmetric-qchat-group-${groupId}` + (data: any) => data.identifier === `symmetric-qchat-group-${groupId}` ); if (filterId?.length === 0) { return false; @@ -38,149 +59,182 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec // Get the most recent date for both a and b const dateA = a.updated ? new Date(a.updated) : new Date(a.created); const dateB = b.updated ? new Date(b.updated) : new Date(b.created); - + // Sort by most recent return dateB.getTime() - dateA.getTime(); }); - + return sortedData[0]; }; - const getSecretKey = async (loadingGroupParam?: boolean, secretKeyToPublish?: boolean) => { + + const getSecretKey = async ( + loadingGroupParam?: boolean, + secretKeyToPublish?: boolean + ) => { try { - pauseAllQueues() - - - - const {names} = await getGroupAdmins(groupId); - if(!names.length){ - throw new Error('Network error') + pauseAllQueues(); + + const { names } = await getGroupAdmins(groupId); + if (!names.length) { + throw new Error('Network error'); } const publish = await getPublishesFromAdmins(names); - - + if (publish === false) { - return false; } - + const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier }?encoding=base64&rebuild=true` ); const data = await res.text(); - + const decryptedKey: any = await decryptResource(data); - + const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - + if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); - + throw new Error('SecretKey is not valid'); + if (decryptedKeyToObject) { - return decryptedKeyToObject; - } else { } - } catch (error) { - - - } finally { + console.log(error); } }; - const createCommonSecret = async ()=> { - try { - const fee = await getFee('ARBITRARY') - await show({ - message: "Would you like to perform an ARBITRARY transaction?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoading(true) + const createCommonSecret = async () => { + try { + const fee = await getFee('ARBITRARY'); + await show({ + message: 'Would you like to perform an ARBITRARY transaction?', + publishFee: fee.fee + ' QORT', + }); + setIsLoading(true); - const secretKey2 = await getSecretKey() - if((!secretKey2 && secretKey2 !== false)) throw new Error('invalid secret key') - if (secretKey2 && !validateSecretKey(secretKey2)) throw new Error('invalid secret key') + const secretKey2 = await getSecretKey(); + if (!secretKey2 && secretKey2 !== false) + throw new Error('invalid secret key'); + if (secretKey2 && !validateSecretKey(secretKey2)) + throw new Error('invalid secret key'); - const secretKeyToSend = !secretKey2 ? null : secretKey2 - - - window.sendMessage("encryptAndPublishSymmetricKeyGroupChat", { - groupId: groupId, - previousData: secretKeyToSend, - }) - .then((response) => { - if (!response?.error) { - setInfoSnack({ - type: "success", - message: "Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.", - }); - setOpenSnack(true); - setTxList((prev) => [ - { - ...response, - type: 'created-common-secret', - label: `Published secret key for group ${groupId}: awaiting confirmation`, - labelDone: `Published secret key for group ${groupId}: success!`, - done: false, - groupId, - }, - ...prev, - ]); - } - setIsLoading(false); - setTimeout(() => { - setIsForceShowCreationKeyPopup(false) - }, 1000); - }) - .catch((error) => { - console.error("Failed to encrypt and publish symmetric key for group chat:", error.message || "An error occurred"); - setIsLoading(false); + const secretKeyToSend = !secretKey2 ? null : secretKey2; + + window + .sendMessage('encryptAndPublishSymmetricKeyGroupChat', { + groupId: groupId, + previousData: secretKeyToSend, + }) + .then((response) => { + if (!response?.error) { + setInfoSnack({ + type: 'success', + message: + 'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.', }); - - } catch (error) { - - } + setOpenSnack(true); + setTxList((prev) => [ + { + ...response, + type: 'created-common-secret', + label: `Published secret key for group ${groupId}: awaiting confirmation`, + labelDone: `Published secret key for group ${groupId}: success!`, + done: false, + groupId, + }, + ...prev, + ]); + } + setIsLoading(false); + setTimeout(() => { + setIsForceShowCreationKeyPopup(false); + }, 1000); + }) + .catch((error) => { + console.error( + 'Failed to encrypt and publish symmetric key for group chat:', + error.message || 'An error occurred' + ); + setIsLoading(false); + }); + } catch (error) { + console.log(error); } - + }; return ( - - Re-encrypt key - {noSecretKey ? ( - - There is no group secret key. Be the first admin to publish one! - - ) : isOwner && secretKeyDetails && userInfo?.name && userInfo.name !== secretKeyDetails?.name ? ( - - The latest group secret key was published by a non-owner. As the owner of the group please re-encrypt the key as a safeguard - - ): isForceShowCreationKeyPopup ? null : ( - - The group member list has changed. Please re-encrypt the secret key. - - )} - - + flexDirection: 'column', + gap: '25px', + maxWidth: '350px', + padding: '25px', + }} + > + + Re-encrypt key + + + {noSecretKey ? ( + + + There is no group secret key. Be the first admin to publish one! + + + ) : isOwner && + secretKeyDetails && + userInfo?.name && + userInfo.name !== secretKeyDetails?.name ? ( + + + The latest group secret key was published by a non-owner. As the + owner of the group please re-encrypt the key as a safeguard + + + ) : isForceShowCreationKeyPopup ? null : ( + + + The group member list has changed. Please re-encrypt the secret key. + + + )} + + + - + + - - ) -} + ); +}; diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 98dd48b..4221af7 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -5,31 +5,22 @@ import React, { useRef, useState, } from 'react'; -import { CreateCommonSecret } from './CreateCommonSecret'; -import { reusableGet } from '../../qdn/publish/pubish'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; import { base64ToUint8Array, objectToBase64, } from '../../qdn/encryption/group-encryption'; -import { ChatContainerComp } from './ChatContainer'; -import { ChatList } from './ChatList'; import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; import Tiptap from './TipTap'; -import { - AuthenticatedContainerInnerTop, - CustomButton, -} from '../../styles/App-styles'; +import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; -import { Box, Typography } from '@mui/material'; +import { Box, Typography, useTheme } from '@mui/material'; import { Spacer } from '../../common/Spacer'; import ShortUniqueId from 'short-unique-id'; import { AnnouncementList } from './AnnouncementList'; -const uid = new ShortUniqueId({ length: 8 }); import CampaignIcon from '@mui/icons-material/Campaign'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { AnnouncementDiscussion } from './AnnouncementDiscussion'; import { MyContext, @@ -42,9 +33,11 @@ import { import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group'; -import { getRootHeight } from '../../utils/mobile/mobileUtils'; + +const uid = new ShortUniqueId({ length: 8 }); export const requestQueueCommentCount = new RequestQueueWithPromise(3); + export const requestQueuePublishedAccouncements = new RequestQueueWithPromise( 3 ); @@ -106,7 +99,9 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; export const handleUnencryptedPublishes = (publishes) => { let publishesData = []; @@ -117,10 +112,13 @@ export const handleUnencryptedPublishes = (publishes) => { if (decodedData) { publishesData.push({ decryptedData: decodedData }); } - } catch (error) {} + } catch (error) { + console.log(error); + } }); return publishesData; }; + export const GroupAnnouncements = ({ selectedGroup, secretKey, @@ -236,7 +234,9 @@ export const GroupAnnouncements = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnc = async ({ encryptedData, identifier }: any) => { @@ -258,6 +258,7 @@ export const GroupAnnouncements = ({ }); }); }; + const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); @@ -286,17 +287,21 @@ export const GroupAnnouncements = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnnouncement = async () => { try { pauseAllQueues(); const fee = await getFee('ARBITRARY'); + await show({ message: 'Would you like to perform a ARBITRARY transaction?', publishFee: fee.fee + ' QORT', }); + if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -379,8 +384,7 @@ export const GroupAnnouncements = ({ ); } } catch (error) { - } finally { - // dispatch(setIsLoadingGlobal(false)) + console.log(error); } }, [secretKey] @@ -422,11 +426,15 @@ export const GroupAnnouncements = ({ isPrivate ); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const interval = useRef(null); + const theme = useTheme(); + const checkNewMessages = React.useCallback(async () => { try { const identifier = `grp-${selectedGroup}-anc-`; @@ -449,7 +457,9 @@ export const GroupAnnouncements = ({ }, isPrivate ); - } catch (error) {} + } catch (error) { + console.log(error); + } } setAnnouncements(responseData); return; @@ -467,11 +477,13 @@ export const GroupAnnouncements = ({ { name: data.name, identifier: data.identifier }, isPrivate ); - } catch (error) {} + } catch (error) { + console.log(error); + } } setAnnouncements((prev) => [...newArray, ...prev]); } catch (error) { - } finally { + console.log(error); } }, [announcements, secretKey, selectedGroup]); @@ -523,10 +535,10 @@ export const GroupAnnouncements = ({ : 'calc(100vh - 70px)', display: 'flex', flexDirection: 'column', - width: '100%', - visibility: hide && 'hidden', - position: hide && 'fixed', left: hide && '-1000px', + position: hide && 'fixed', + visibility: hide && 'hidden', + width: '100%', }} >
{!isMobile && ( Group Announcements )} - +
+ {!isLoading && combinedListTempAndReal?.length === 0 && (
+ {isFocusedParent && ( @@ -678,43 +688,46 @@ export const GroupAnnouncements = ({ // Unfocus the editor }} style={{ - marginTop: 'auto', alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', background: 'var(--danger)', + cursor: isSending ? 'default' : 'pointer', flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', + fontSize: '14px', + marginTop: 'auto', + padding: '5px', }} > {` Close`} )} + { if (isSending) return; publishAnnouncement(); }} style={{ - marginTop: 'auto', alignSelf: 'center', + background: isSending + ? theme.palette.background.default + : theme.palette.background.paper, cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', + fontSize: '14px', + marginTop: 'auto', + padding: '5px', }} > {isSending && ( )} diff --git a/src/components/Chat/GroupForum.tsx b/src/components/Chat/GroupForum.tsx index d8dd7e5..b97b11c 100644 --- a/src/components/Chat/GroupForum.tsx +++ b/src/components/Chat/GroupForum.tsx @@ -1,19 +1,6 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { GroupMail } from "../Group/Forum/GroupMail"; -import { MyContext, isMobile } from "../../App"; -import { getRootHeight } from "../../utils/mobile/mobileUtils"; - - - - - +import { useContext, useEffect, useState } from 'react'; +import { GroupMail } from '../Group/Forum/GroupMail'; +import { MyContext, isMobile } from '../../App'; export const GroupForum = ({ selectedGroup, @@ -23,12 +10,13 @@ export const GroupForum = ({ isAdmin, myAddress, hide, - defaultThread, + defaultThread, setDefaultThread, - isPrivate + isPrivate, }) => { - const { rootHeight } = useContext(MyContext); + const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); + useEffect(() => { if (hide) { setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving @@ -39,20 +27,27 @@ export const GroupForum = ({ return (
- - -
+ style={{ + display: 'flex', + flexDirection: 'column', + height: 'calc(100vh - 70px)', + left: hide && '-1000px', + opacity: hide ? 0 : 1, + position: hide ? 'fixed' : 'relative', + visibility: hide && 'hidden', + width: '100%', + }} + > + +
); }; diff --git a/src/components/Chat/MentionList.tsx b/src/components/Chat/MentionList.tsx index 85f6890..06cea5c 100644 --- a/src/components/Chat/MentionList.tsx +++ b/src/components/Chat/MentionList.tsx @@ -1,69 +1,68 @@ -import React, { - forwardRef, useEffect, useImperativeHandle, - useState, - } from 'react' - - export default forwardRef((props, ref) => { - const [selectedIndex, setSelectedIndex] = useState(0) - - const selectItem = index => { - const item = props.items[index] - - if (item) { - props.command(item) +import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; + +export default forwardRef((props, ref) => { + const [selectedIndex, setSelectedIndex] = useState(0); + + const selectItem = (index) => { + const item = props.items[index]; + + if (item) { + props.command(item); + } + }; + + const upHandler = () => { + setSelectedIndex( + (selectedIndex + props.items.length - 1) % props.items.length + ); + }; + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props.items.length); + }; + + const enterHandler = () => { + selectItem(selectedIndex); + }; + + useEffect(() => setSelectedIndex(0), [props.items]); + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }) => { + if (event.key === 'ArrowUp') { + upHandler(); + return true; } - } - - const upHandler = () => { - setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length) - } - - const downHandler = () => { - setSelectedIndex((selectedIndex + 1) % props.items.length) - } - - const enterHandler = () => { - selectItem(selectedIndex) - } - - useEffect(() => setSelectedIndex(0), [props.items]) - - useImperativeHandle(ref, () => ({ - onKeyDown: ({ event }) => { - if (event.key === 'ArrowUp') { - upHandler() - return true - } - - if (event.key === 'ArrowDown') { - downHandler() - return true - } - - if (event.key === 'Enter') { - enterHandler() - return true - } - - return false - }, - })) - - return ( -
- {props.items.length - ? props.items.map((item, index) => ( - - )) - :
No result
- } -
- ) - }) - \ No newline at end of file + + if (event.key === 'ArrowDown') { + downHandler(); + return true; + } + + if (event.key === 'Enter') { + enterHandler(); + return true; + } + + return false; + }, + })); + + return ( +
+ {props.items.length ? ( + props.items.map((item, index) => ( + + )) + ) : ( +
No result
+ )} +
+ ); +}); diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx index 52d0d93..2bf3d6f 100644 --- a/src/components/Chat/MessageDisplay.tsx +++ b/src/components/Chat/MessageDisplay.tsx @@ -1,28 +1,29 @@ -import React, { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import DOMPurify from 'dompurify'; -import './styles.css'; +import './chat.css'; import { executeEvent } from '../../utils/events'; import { Embed } from '../Embeds/Embed'; +import { Box, useTheme } from '@mui/material'; export const extractComponents = (url) => { - if (!url || !url.startsWith("qortal://")) { + if (!url || !url.startsWith('qortal://')) { return null; } // Skip links starting with "qortal://use-" - if (url.startsWith("qortal://use-")) { + if (url.startsWith('qortal://use-')) { return null; } - url = url.replace(/^(qortal\:\/\/)/, ""); - if (url.includes("/")) { - let parts = url.split("/"); + url = url.replace(/^(qortal\:\/\/)/, ''); + if (url.includes('/')) { + let parts = url.split('/'); const service = parts[0].toUpperCase(); parts.shift(); const name = parts[0]; parts.shift(); let identifier; - const path = parts.join("/"); + const path = parts.join('/'); return { service, name, identifier, path }; } @@ -64,8 +65,7 @@ function processText(input) { } const linkify = (text) => { - if (!text) return ""; // Return an empty string if text is null or undefined - + if (!text) return ''; // Return an empty string if text is null or undefined let textFormatted = text; const urlPattern = /(\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+)/g; textFormatted = text.replace(urlPattern, (url) => { @@ -75,22 +75,68 @@ const linkify = (text) => { return processText(textFormatted); }; - export const MessageDisplay = ({ htmlContent, isReply }) => { + const theme = useTheme(); - - const sanitizedContent = useMemo(()=> { + const sanitizedContent = useMemo(() => { return DOMPurify.sanitize(linkify(htmlContent), { ALLOWED_TAGS: [ - 'a', 'b', 'i', 'em', 'strong', 'p', 'br', 'div', 'span', 'img', - 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'code', 'pre', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 's', 'hr' + 'a', + 'b', + 'i', + 'em', + 'strong', + 'p', + 'br', + 'div', + 'span', + 'img', + 'ul', + 'ol', + 'li', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'blockquote', + 'code', + 'pre', + 'table', + 'thead', + 'tbody', + 'tr', + 'th', + 'td', + 's', + 'hr', ], ALLOWED_ATTR: [ - 'href', 'target', 'rel', 'class', 'src', 'alt', 'title', - 'width', 'height', 'style', 'align', 'valign', 'colspan', 'rowspan', 'border', 'cellpadding', 'cellspacing', 'data-url' + 'href', + 'target', + 'rel', + 'class', + 'src', + 'alt', + 'title', + 'width', + 'height', + 'style', + 'align', + 'valign', + 'colspan', + 'rowspan', + 'border', + 'cellpadding', + 'cellspacing', + 'data-url', ], - }).replace(/]*data-url="qortal:\/\/use-embed\/[^"]*"[^>]*>.*?<\/span>/g, ''); - }, [htmlContent]) + }).replace( + /]*data-url="qortal:\/\/use-embed\/[^"]*"[^>]*>.*?<\/span>/g, + '' + ); + }, [htmlContent]); const handleClick = async (e) => { e.preventDefault(); @@ -98,7 +144,7 @@ export const MessageDisplay = ({ htmlContent, isReply }) => { const target = e.target; if (target.tagName === 'A') { const href = target.getAttribute('href'); - if(window?.electronAPI){ + if (window?.electronAPI) { window.electronAPI.openExternal(href); } else { window.open(href, '_system'); @@ -106,32 +152,32 @@ export const MessageDisplay = ({ htmlContent, isReply }) => { } else if (target.getAttribute('data-url')) { const url = target.getAttribute('data-url'); - let copyUrl = url + let copyUrl = url; - try { - copyUrl = copyUrl.replace(/^(qortal:\/\/)/, '') - if (copyUrl.startsWith('use-')) { - // Handle the new 'use' format - const parts = copyUrl.split('/') - const type = parts[0].split('-')[1] // e.g., 'group' from 'use-group' - parts.shift() - const action = parts.length > 0 ? parts[0].split('-')[1] : null // e.g., 'invite' from 'action-invite' - parts.shift() - const idPrefix = parts.length > 0 ? parts[0].split('-')[0] : null // e.g., 'groupid' from 'groupid-321' - const id = parts.length > 0 ? parts[0].split('-')[1] : null // e.g., '321' from 'groupid-321' - if(action === 'join'){ - executeEvent("globalActionJoinGroup", { groupId: id}); - return + try { + copyUrl = copyUrl.replace(/^(qortal:\/\/)/, ''); + if (copyUrl.startsWith('use-')) { + // Handle the new 'use' format + const parts = copyUrl.split('/'); + const type = parts[0].split('-')[1]; // e.g., 'group' from 'use-group' + parts.shift(); + const action = parts.length > 0 ? parts[0].split('-')[1] : null; // e.g., 'invite' from 'action-invite' + parts.shift(); + const idPrefix = parts.length > 0 ? parts[0].split('-')[0] : null; // e.g., 'groupid' from 'groupid-321' + const id = parts.length > 0 ? parts[0].split('-')[1] : null; // e.g., '321' from 'groupid-321' + if (action === 'join') { + executeEvent('globalActionJoinGroup', { groupId: id }); + return; + } } + } catch (error) { + //error } - } catch (error) { - //error - } const res = extractComponents(url); if (res) { const { service, name, identifier, path } = res; - executeEvent("addTab", { data: { service, name, identifier, path } }); - executeEvent("open-apps-mode", { }); + executeEvent('addTab', { data: { service, name, identifier, path } }); + executeEvent('open-apps-mode', {}); } } }; @@ -141,19 +187,24 @@ export const MessageDisplay = ({ htmlContent, isReply }) => { let embedData = null; if (embedLink) { - embedData = embedLink[0] + embedData = embedLink[0]; } return ( - <> - {embedLink && ( - - )} -
- + + {embedLink && } +
+ ); }; diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 4d9019c..a40fca8 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -1,573 +1,653 @@ -import { Message } from "@chatscope/chat-ui-kit-react"; -import React, { useCallback, useContext, useEffect, useMemo, useState } from "react"; -import { useInView } from "react-intersection-observer"; -import { MessageDisplay } from "./MessageDisplay"; -import { Avatar, Box, Button, ButtonBase, List, ListItem, ListItemText, Popover, Tooltip, Typography } from "@mui/material"; -import { formatTimestamp } from "../../utils/time"; -import { getBaseApi } from "../../background"; -import { MyContext, getBaseApiReact } from "../../App"; -import { generateHTML } from "@tiptap/react"; -import Highlight from "@tiptap/extension-highlight"; -import Mention from "@tiptap/extension-mention"; -import StarterKit from "@tiptap/starter-kit"; -import Underline from "@tiptap/extension-underline"; -import { executeEvent } from "../../utils/events"; -import { WrapperUserAction } from "../WrapperUserAction"; -import ReplyIcon from "@mui/icons-material/Reply"; -import { Spacer } from "../../common/Spacer"; -import { ReactionPicker } from "../ReactionPicker"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { useInView } from 'react-intersection-observer'; +import { MessageDisplay } from './MessageDisplay'; +import { + Avatar, + Box, + Button, + ButtonBase, + List, + ListItem, + ListItemText, + Popover, + Tooltip, + Typography, + useTheme, +} from '@mui/material'; +import { formatTimestamp } from '../../utils/time'; +import { MyContext, getBaseApiReact } from '../../App'; +import { generateHTML } from '@tiptap/react'; +import Highlight from '@tiptap/extension-highlight'; +import Mention from '@tiptap/extension-mention'; +import StarterKit from '@tiptap/starter-kit'; +import Underline from '@tiptap/extension-underline'; +import { WrapperUserAction } from '../WrapperUserAction'; +import ReplyIcon from '@mui/icons-material/Reply'; +import { Spacer } from '../../common/Spacer'; +import { ReactionPicker } from '../ReactionPicker'; import KeyOffIcon from '@mui/icons-material/KeyOff'; import EditIcon from '@mui/icons-material/Edit'; import TextStyle from '@tiptap/extension-text-style'; -import { addressInfoKeySelector } from "../../atoms/global"; -import { useRecoilValue } from "recoil"; -import level0Img from "../../assets/badges/level-0.png" -import level1Img from "../../assets/badges/level-1.png" -import level2Img from "../../assets/badges/level-2.png" -import level3Img from "../../assets/badges/level-3.png" -import level4Img from "../../assets/badges/level-4.png" -import level5Img from "../../assets/badges/level-5.png" -import level6Img from "../../assets/badges/level-6.png" -import level7Img from "../../assets/badges/level-7.png" -import level8Img from "../../assets/badges/level-8.png" -import level9Img from "../../assets/badges/level-9.png" -import level10Img from "../../assets/badges/level-10.png" +import level0Img from '../../assets/badges/level-0.png'; +import level1Img from '../../assets/badges/level-1.png'; +import level2Img from '../../assets/badges/level-2.png'; +import level3Img from '../../assets/badges/level-3.png'; +import level4Img from '../../assets/badges/level-4.png'; +import level5Img from '../../assets/badges/level-5.png'; +import level6Img from '../../assets/badges/level-6.png'; +import level7Img from '../../assets/badges/level-7.png'; +import level8Img from '../../assets/badges/level-8.png'; +import level9Img from '../../assets/badges/level-9.png'; +import level10Img from '../../assets/badges/level-10.png'; -const getBadgeImg = (level)=> { - switch(level?.toString()){ - - case '0': return level0Img - case '1': return level1Img - case '2': return level2Img - case '3': return level3Img - case '4': return level4Img - case '5': return level5Img - case '6': return level6Img - case '7': return level7Img - case '8': return level8Img - case '9': return level9Img - case '10': return level10Img - default: return level0Img +const getBadgeImg = (level) => { + switch (level?.toString()) { + case '0': + return level0Img; + case '1': + return level1Img; + case '2': + return level2Img; + case '3': + return level3Img; + case '4': + return level4Img; + case '5': + return level5Img; + case '6': + return level6Img; + case '7': + return level7Img; + case '8': + return level8Img; + case '9': + return level9Img; + case '10': + return level10Img; + default: + return level0Img; } -} -export const MessageItem = React.memo(({ - message, - onSeen, - isLast, - isTemp, - myAddress, - onReply, - isShowingAsReply, - reply, - replyIndex, - scrollToItem, - handleReaction, - reactions, - isUpdating, - lastSignature, - onEdit, - isPrivate -}) => { +}; -const {getIndividualUserInfo} = useContext(MyContext) - const [anchorEl, setAnchorEl] = useState(null); - const [selectedReaction, setSelectedReaction] = useState(null); - const [userInfo, setUserInfo] = useState(null) +export const MessageItem = React.memo( + ({ + message, + onSeen, + isLast, + isTemp, + myAddress, + onReply, + isShowingAsReply, + reply, + replyIndex, + scrollToItem, + handleReaction, + reactions, + isUpdating, + lastSignature, + onEdit, + isPrivate, + }) => { + const { getIndividualUserInfo } = useContext(MyContext); + const [anchorEl, setAnchorEl] = useState(null); + const [selectedReaction, setSelectedReaction] = useState(null); + const [userInfo, setUserInfo] = useState(null); -useEffect(()=> { - const getInfo = async ()=> { - if(!message?.sender) return - try { - const res = await getIndividualUserInfo(message?.sender) - if(!res) return null - setUserInfo(res) - } catch (error) { - // - } - } + useEffect(() => { + const getInfo = async () => { + if (!message?.sender) return; + try { + const res = await getIndividualUserInfo(message?.sender); + if (!res) return null; + setUserInfo(res); + } catch (error) { + // + } + }; - getInfo() -}, [message?.sender, getIndividualUserInfo]) + getInfo(); + }, [message?.sender, getIndividualUserInfo]); -const htmlText = useMemo(()=> { - - if(message?.messageText){ - return generateHTML(message?.messageText, [ - StarterKit, - Underline, - Highlight, - Mention, - TextStyle - ]) - } - -}, [message?.editTimestamp]) + const htmlText = useMemo(() => { + if (message?.messageText) { + return generateHTML(message?.messageText, [ + StarterKit, + Underline, + Highlight, + Mention, + TextStyle, + ]); + } + }, [message?.editTimestamp]); + const htmlReply = useMemo(() => { + if (reply?.messageText) { + return generateHTML(reply?.messageText, [ + StarterKit, + Underline, + Highlight, + Mention, + TextStyle, + ]); + } + }, [reply?.editTimestamp]); + const userAvatarUrl = useMemo(() => { + return message?.senderName + ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${ + message?.senderName + }/qortal_avatar?async=true` + : ''; + }, []); -const htmlReply = useMemo(()=> { - - if(reply?.messageText){ - return generateHTML(reply?.messageText, [ - StarterKit, - Underline, - Highlight, - Mention, - TextStyle - ]) - } - -}, [reply?.editTimestamp]) + const onSeenFunc = useCallback(() => { + onSeen(message.id); + }, [message?.id]); -const userAvatarUrl = useMemo(()=> { - return message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${ - message?.senderName - }/qortal_avatar?async=true` : '' -}, []) + const theme = useTheme(); -const onSeenFunc = useCallback(()=> { - onSeen(message.id); -}, [message?.id]) + return ( + <> + {message?.divide && ( +
+ Unread messages below +
+ )} - - return ( - <> - {message?.divide && ( -
- Unread messages below -
- )} - - - - -
- {isShowingAsReply ? ( - - ) : ( - - - - - {message?.senderName?.charAt(0)} - - - - - - - - - - - )} - - - - - - {message?.senderName || message?.sender} - - - - - {message?.sender === myAddress && (!message?.isNotEncrypted || isPrivate === false) && ( - { - onEdit(message); - }} - > - - - )} - {!isShowingAsReply && ( - { - onReply(message); - }} - > - - - )} - {!isShowingAsReply && handleReaction && ( - { - - if(reactions && reactions[val] && reactions[val]?.find((item)=> item?.sender === myAddress)){ - handleReaction(val, message, false) - } else { - handleReaction(val, message, true) - } - - }} /> - )} - - - {reply && ( - <> - - { - scrollToItem(replyIndex) - - + gap: '7px', + opacity: isTemp || isUpdating ? 0.5 : 1, + padding: '10px', + width: '95%', }} + id={message?.signature} > - - - Replied to {reply?.senderName || reply?.senderAddress} - {reply?.messageText && ( - - )} - {reply?.decryptedData?.type === "notification" ? ( - - ) : ( - - )} - - - - )} - {htmlText && ( - - )} - - - {message?.decryptedData?.type === "notification" ? ( - - ) : ( - - )} - - - {reactions && Object.keys(reactions).map((reaction)=> { - const numberOfReactions = reactions[reaction]?.length - // const myReaction = reactions - if(numberOfReactions === 0) return null - return ( - { - event.stopPropagation(); // Prevent event bubbling - setAnchorEl(event.currentTarget); - setSelectedReaction(reaction); - }}> -
{reaction}
{numberOfReactions > 1 && ( - {' '} {numberOfReactions} - )} -
- ) - })} -
- {selectedReaction && ( - { - setAnchorEl(null); - setSelectedReaction(null); - }} - anchorOrigin={{ - vertical: "top", - horizontal: "center", - }} - transformOrigin={{ - vertical: "bottom", - horizontal: "center", - }} - PaperProps={{ - style: { - backgroundColor: "#232428", - color: "white", - }, - }} - > - - - People who reacted with {selectedReaction} - - - {reactions[selectedReaction]?.map((reactionItem) => ( - - - - ))} - - + + {message?.senderName?.charAt(0)} + + + + + - - )} - - {message?.isNotEncrypted && isPrivate && ( - )} - - {isUpdating ? ( - - {message?.status === 'failed-permanent' ? 'Failed to update' : 'Updating...'} - - ) : isTemp ? ( - - {message?.status === 'failed-permanent' ? 'Failed to send' : 'Sending...'} - - ) : ( - <> - {message?.isEdit && ( - - Edited - - )} - - {formatTimestamp(message.timestamp)} - - - )} - -
-
- -
-
- - ); -}); + + + + + {message?.senderName || message?.sender} + + + + {message?.sender === myAddress && + (!message?.isNotEncrypted || isPrivate === false) && ( + { + onEdit(message); + }} + > + + + )} + {!isShowingAsReply && ( + { + onReply(message); + }} + > + + + )} + {!isShowingAsReply && handleReaction && ( + { + if ( + reactions && + reactions[val] && + reactions[val]?.find( + (item) => item?.sender === myAddress + ) + ) { + handleReaction(val, message, false); + } else { + handleReaction(val, message, true); + } + }} + /> + )} + + + {reply && ( + <> + + { + scrollToItem(replyIndex); + }} + > + + + + Replied to {reply?.senderName || reply?.senderAddress} + + {reply?.messageText && ( + + )} + {reply?.decryptedData?.type === 'notification' ? ( + + ) : ( + + )} + + + + )} + {htmlText && } + {message?.decryptedData?.type === 'notification' ? ( + + ) : ( + + )} -export const ReplyPreview = ({message, isEdit})=> { + + + {reactions && + Object.keys(reactions).map((reaction) => { + const numberOfReactions = reactions[reaction]?.length; + // const myReaction = reactions + if (numberOfReactions === 0) return null; + return ( + { + event.stopPropagation(); // Prevent event bubbling + setAnchorEl(event.currentTarget); + setSelectedReaction(reaction); + }} + > +
+ {reaction} +
{' '} + {numberOfReactions > 1 && ( + + {' '} + {numberOfReactions} + + )} +
+ ); + })} +
+ + {selectedReaction && ( + { + setAnchorEl(null); + setSelectedReaction(null); + }} + anchorOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + transformOrigin={{ + vertical: 'bottom', + horizontal: 'center', + }} + PaperProps={{ + // TODO: deprecated + style: { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + }} + > + + + People who reacted with {selectedReaction} + + + + {reactions[selectedReaction]?.map((reactionItem) => ( + + + + ))} + + + + + )} + + {message?.isNotEncrypted && isPrivate && ( + + )} + + {isUpdating ? ( + + {message?.status === 'failed-permanent' + ? 'Failed to update' + : 'Updating...'} + + ) : isTemp ? ( + + {message?.status === 'failed-permanent' + ? 'Failed to send' + : 'Sending...'} + + ) : ( + <> + {message?.isEdit && ( + + Edited + + )} + + {formatTimestamp(message.timestamp)} + + + )} + +
+
+
+ + + ); + } +); + +export const ReplyPreview = ({ message, isEdit = false }) => { + const theme = useTheme(); return ( + + + {isEdit ? ( + - - - {isEdit ? ( - Editing Message - ) : ( - Replied to {message?.senderName || message?.senderAddress} - )} - - {message?.messageText && ( - - )} - {message?.decryptedData?.type === "notification" ? ( - - ) : ( - - )} - - - - ) -} + Editing Message + + ) : ( + + Replied to {message?.senderName || message?.senderAddress} + + )} -const MessageWragger = ({lastMessage, onSeen, isLast, children})=> { + {message?.messageText && ( + + )} - if(lastMessage){ + {message?.decryptedData?.type === 'notification' ? ( + + ) : ( + + )} + + + ); +}; + +const MessageWragger = ({ lastMessage, onSeen, isLast, children }) => { + if (lastMessage) { return ( - {children} - ) + + {children} + + ); } - return children -} + return children; +}; -const WatchComponent = ({onSeen, isLast, children})=> { +const WatchComponent = ({ onSeen, isLast, children }) => { const { ref, inView } = useInView({ threshold: 0.7, // Fully visible triggerOnce: true, // Only trigger once when it becomes visible @@ -577,20 +657,22 @@ const WatchComponent = ({onSeen, isLast, children})=> { useEffect(() => { if (inView && isLast && onSeen) { - - setTimeout(() => { - onSeen(); + setTimeout(() => { + onSeen(); }, 100); - } }, [inView, isLast, onSeen]); - return
- {children} -
- -} \ No newline at end of file + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/Chat/ResizableImage.tsx b/src/components/Chat/ResizableImage.tsx index 8c29292..881c1d7 100644 --- a/src/components/Chat/ResizableImage.tsx +++ b/src/components/Chat/ResizableImage.tsx @@ -1,8 +1,10 @@ -import React, { useRef } from 'react'; +import { useRef } from 'react'; import { NodeViewWrapper } from '@tiptap/react'; +import { useTheme } from '@mui/material'; const ResizableImage = ({ node, updateAttributes, selected }) => { const imgRef = useRef(null); + const theme = useTheme(); const startResizing = (e) => { e.preventDefault(); @@ -40,18 +42,23 @@ const ResizableImage = ({ node, updateAttributes, selected }) => { src={node.attrs.src} alt={node.attrs.alt || ''} title={node.attrs.title || ''} - style={{ width: node.attrs.width || 'auto', display: 'block', margin: '0 auto' }} + style={{ + width: node.attrs.width || 'auto', + display: 'block', + margin: '0 auto', + }} draggable={false} // Prevent image dragging /> +
{ + +const MenuBar = ({ + setEditorRef, + isChat, + isDisabledEditorEnter, + setIsDisabledEditorEnter, +}) => { const { editor } = useCurrentEditor(); const fileInputRef = useRef(null); + const theme = useTheme(); if (!editor) { return null; @@ -67,15 +68,15 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi new Compressor(file, { quality: 0.6, maxWidth: 1200, - mimeType: "image/webp", + mimeType: 'image/webp', success(result) { - compressedFile = new File([result], "image.webp", { - type: "image/webp", + compressedFile = new File([result], 'image.webp', { + type: 'image/webp', }); resolve(); }, error(err) { - console.error("Image compression error:", err); + console.error('Image compression error:', err); }, }); }); @@ -87,9 +88,9 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor .chain() .focus() - .setImage({ src: url, style: "width: auto" }) + .setImage({ src: url, style: 'width: auto' }) .run(); - fileInputRef.current.value = ""; + fileInputRef.current.value = ''; }; reader.readAsDataURL(compressedFile); } @@ -102,7 +103,7 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi const handlePaste = (event) => { const items = event.clipboardData.items; for (const item of items) { - if (item.type.startsWith("image/")) { + if (item.type.startsWith('image/')) { const file = item.getAsFile(); if (file) { event.preventDefault(); // Prevent the default paste behavior @@ -114,24 +115,29 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi useEffect(() => { if (editor) { - editor.view.dom.addEventListener("paste", handlePaste); + editor.view.dom.addEventListener('paste', handlePaste); return () => { - editor.view.dom.removeEventListener("paste", handlePaste); + editor.view.dom.removeEventListener('paste', handlePaste); }; } }, [editor]); return (
-
+
editor.chain().focus().toggleBold().run()} disabled={!editor.can().chain().focus().toggleBold().run()} sx={{ - color: editor.isActive("bold") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('bold') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -140,8 +146,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi onClick={() => editor.chain().focus().toggleItalic().run()} disabled={!editor.can().chain().focus().toggleItalic().run()} sx={{ - color: editor.isActive("italic") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('italic') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -150,8 +158,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi onClick={() => editor.chain().focus().toggleStrike().run()} disabled={!editor.can().chain().focus().toggleStrike().run()} sx={{ - color: editor.isActive("strike") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('strike') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -160,8 +170,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi onClick={() => editor.chain().focus().toggleCode().run()} disabled={!editor.can().chain().focus().toggleCode().run()} sx={{ - color: editor.isActive("code") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('code') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -170,13 +182,13 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi onClick={() => editor.chain().focus().unsetAllMarks().run()} sx={{ color: - editor.isActive("bold") || - editor.isActive("italic") || - editor.isActive("strike") || - editor.isActive("code") - ? "white" - : "gray", - padding: isMobile ? "5px" : "revert", + editor.isActive('bold') || + editor.isActive('italic') || + editor.isActive('strike') || + editor.isActive('code') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -184,8 +196,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().toggleBulletList().run()} sx={{ - color: editor.isActive("bulletList") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('bulletList') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -193,8 +207,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().toggleOrderedList().run()} sx={{ - color: editor.isActive("orderedList") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('orderedList') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -202,8 +218,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().toggleCodeBlock().run()} sx={{ - color: editor.isActive("codeBlock") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('codeBlock') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -211,8 +229,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().toggleBlockquote().run()} sx={{ - color: editor.isActive("blockquote") ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('blockquote') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -220,7 +240,7 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().setHorizontalRule().run()} disabled={!editor.can().chain().focus().setHorizontalRule().run()} - sx={{ color: "gray", padding: isMobile ? "5px" : "revert" }} + sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }} > @@ -229,8 +249,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().toggleHeading({ level: 1 }).run() } sx={{ - color: editor.isActive("heading", { level: 1 }) ? "white" : "gray", - padding: isMobile ? "5px" : "revert", + color: editor.isActive('heading', { level: 1 }) + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: isMobile ? '5px' : 'revert', }} > @@ -238,66 +260,68 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi editor.chain().focus().undo().run()} disabled={!editor.can().chain().focus().undo().run()} - sx={{ color: "gray", padding: isMobile ? "5px" : "revert" }} + sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().redo().run()} disabled={!editor.can().chain().focus().redo().run()} - sx={{ color: "gray" }} + sx={{ color: 'gray' }} > {isChat && ( { - setIsDisabledEditorEnter(!isDisabledEditorEnter) - }} - > - - - disable enter - - + sx={{ + display: 'flex', + alignItems: 'center', + marginLeft: '5px', + cursor: 'pointer', + }} + onClick={() => { + setIsDisabledEditorEnter(!isDisabledEditorEnter); + }} + > + + + disable enter + + )} {!isChat && ( <> handleImageUpload(event.target.files[0])} accept="image/*" /> @@ -322,7 +346,7 @@ const extensions = [ }, }), Placeholder.configure({ - placeholder: "Start typing here...", + placeholder: 'Start typing here...', }), ImageResize, ]; @@ -340,12 +364,13 @@ export default ({ overrideMobile, customEditorHeight, membersWithNames, - enableMentions + enableMentions, }) => { - const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState(isDisabledEditorEnterAtom) - + const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState( + isDisabledEditorEnterAtom + ); const extensionsFiltered = isChat - ? extensions.filter((item) => item?.name !== "image") + ? extensions.filter((item) => item?.name !== 'image') : extensions; const editorRef = useRef(null); const setEditorRefFunc = (editorInstance) => { @@ -359,20 +384,14 @@ export default ({ // { id: 3, label: 'Charlie' }, // ]; - - - const users = useMemo(()=> { - return (membersWithNames || [])?.map((item)=> { + const users = useMemo(() => { + return (membersWithNames || [])?.map((item) => { return { id: item, - label: item - } - }) - }, [membersWithNames]) - - - - + label: item, + }; + }); + }, [membersWithNames]); const usersRef = useRef([]); useEffect(() => { @@ -386,13 +405,13 @@ export default ({ const handleBlur = () => { const htmlContent = editorRef.current.getHTML(); - if (!htmlContent?.trim() || htmlContent?.trim() === "

") { + if (!htmlContent?.trim() || htmlContent?.trim() === '

') { // Set focus state based on content } }; - const additionalExtensions = useMemo(()=> { - if(!enableMentions) return [] + const additionalExtensions = useMemo(() => { + if (!enableMentions) return []; return [ Mention.configure({ HTMLAttributes: { @@ -409,122 +428,129 @@ export default ({ let popup; // Reference to the Tippy.js instance let component; - return { - onStart: props => { - component = new ReactRenderer(MentionList, { - props, - editor: props.editor, - }) - - if (!props.clientRect) { - return - } - - popup = tippy('body', { - getReferenceClientRect: props.clientRect, - appendTo: () => document.body, - content: component.element, - showOnCreate: true, - interactive: true, - trigger: 'manual', - placement: 'bottom-start', - }) - }, - - onUpdate(props) { - component.updateProps(props) - - if (!props.clientRect) { - return - } - - popup[0].setProps({ - getReferenceClientRect: props.clientRect, - }) - }, - - onKeyDown(props) { - if (props.event.key === 'Escape') { - popup[0].hide() - - return true - } - - return component.ref?.onKeyDown(props) - }, - - onExit() { - popup[0].destroy() - component.destroy() - }, - } + return { + onStart: (props) => { + component = new ReactRenderer(MentionList, { + props, + editor: props.editor, + }); + + if (!props.clientRect) { + return; + } + + popup = tippy('body', { + getReferenceClientRect: props.clientRect, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }); + }, + + onUpdate(props) { + component.updateProps(props); + + if (!props.clientRect) { + return; + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }); + }, + + onKeyDown(props) { + if (props.event.key === 'Escape') { + popup[0].hide(); + + return true; + } + + return component.ref?.onKeyDown(props); + }, + + onExit() { + popup[0].destroy(); + component.destroy(); + }, + }; }, }, - }) - ] - }, [enableMentions]) + }), + ]; + }, [enableMentions]); - const handleSetIsDisabledEditorEnter = useCallback((val)=> { - setIsDisabledEditorEnter(val) + const handleSetIsDisabledEditorEnter = useCallback((val) => { + setIsDisabledEditorEnter(val); localStorage.setItem('settings-disable-editor-enter', JSON.stringify(val)); - - }, []) - + }, []); return ( -
- - ) - } - extensions={[...extensionsFiltered, ...additionalExtensions - ]} - content={content} - onCreate={({ editor }) => { - editor.on("focus", handleFocus); // Listen for focus event - editor.on("blur", handleBlur); // Listen for blur event +
{ - editor.on('focus', handleFocus); // Ensure focus is updated - editor.on('blur', handleBlur); // Ensure blur is updated - }} - editorProps={{ - attributes: { - class: "tiptap-prosemirror", - style: - isMobile ? - `overflow: auto; min-height: ${ - customEditorHeight ? "200px" : "0px" - }; max-height:calc(100svh - ${customEditorHeight || "140px"})`: `overflow: auto; max-height: 250px`, - }, - handleKeyDown(view, event) { - if (!disableEnter && !isDisabledEditorEnter && event.key === "Enter") { - if (event.shiftKey) { - view.dispatch( - view.state.tr.replaceSelectionWith( - view.state.schema.nodes.hardBreak.create() - ) - ); - return true; - } else { - if (typeof onEnter === "function") { - onEnter(); + > + + ) + } + extensions={[...extensionsFiltered, ...additionalExtensions]} + content={content} + onCreate={({ editor }) => { + editor.on('focus', handleFocus); // Listen for focus event + editor.on('blur', handleBlur); // Listen for blur event + }} + onUpdate={({ editor }) => { + editor.on('focus', handleFocus); // Ensure focus is updated + editor.on('blur', handleBlur); // Ensure blur is updated + }} + editorProps={{ + attributes: { + class: 'tiptap-prosemirror', + style: isMobile + ? `overflow: auto; min-height: ${ + customEditorHeight ? '200px' : '0px' + }; max-height:calc(100svh - ${customEditorHeight || '140px'})` + : `overflow: auto; max-height: 250px`, + }, + handleKeyDown(view, event) { + if ( + !disableEnter && + !isDisabledEditorEnter && + event.key === 'Enter' + ) { + if (event.shiftKey) { + view.dispatch( + view.state.tr.replaceSelectionWith( + view.state.schema.nodes.hardBreak.create() + ) + ); + return true; + } else { + if (typeof onEnter === 'function') { + onEnter(); + } + return true; } - return true; } - } - return false; - }, - }} - /> + return false; + }, + }} + />
- ); }; diff --git a/src/components/Chat/styles.css b/src/components/Chat/chat.css similarity index 66% rename from src/components/Chat/styles.css rename to src/components/Chat/chat.css index 3c7c570..d563d85 100644 --- a/src/components/Chat/styles.css +++ b/src/components/Chat/chat.css @@ -1,6 +1,6 @@ .tiptap { margin-top: 0; - color: white; /* Set default font color to white */ + color: var(--text-primary); width: 100%; } @@ -26,7 +26,7 @@ line-height: 1.1; margin-top: 2.5rem; text-wrap: pretty; - color: white; /* Ensure heading font color is white */ + color: var(--text-primary); } .tiptap h1, @@ -55,18 +55,18 @@ /* Code and preformatted text styles */ .tiptap code { - background-color: #27282c; /* Set code background color to #27282c */ + background-color: var(--background-default); border-radius: 0.4rem; - color: white; /* Ensure inline code text color is white */ + color: var(--text-primary); font-size: 0.85rem; padding: 0.25em 0.3em; text-wrap: pretty; } .tiptap pre { - background: #27282c; /* Set code block background color to #27282c */ + background: var(--background-default); border-radius: 0.5rem; - color: white; /* Ensure code block text color is white */ + color: var(--text-primary); font-family: 'JetBrainsMono', monospace; margin: 1.5rem 0; padding: 0.75rem 1rem; @@ -86,7 +86,7 @@ border-left: 3px solid var(--gray-3); margin: 1.5rem 0; padding-left: 1rem; - color: white; /* Ensure blockquote text color is white */ + color: var(--text-primary); text-wrap: pretty; } @@ -102,49 +102,49 @@ .tiptap p { font-size: 16px; - color: white; /* Ensure paragraph text color is white */ + color: var(--text-primary); margin: 0px; } - .tiptap p.is-editor-empty:first-child::before { - color: #adb5bd; - content: attr(data-placeholder); - float: left; - height: 0; - pointer-events: none; - } - .tiptap p:empty::before { - content: ''; - display: inline-block; - } +.tiptap p.is-editor-empty:first-child::before { + color: var(--text-primary); + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} + +.tiptap p:empty::before { + content: ''; + display: inline-block; +} .tiptap a { - color: cadetblue + color: cadetblue; } .tiptap img { display: block; - max-width: 100%; + max-width: 100%; } .isReply p { font-size: 12px !important; } -.tiptap [data-type="mention"] { +.tiptap [data-type='mention'] { box-decoration-break: clone; - color: lightblue; + color: var(--text-secondary); padding: 0.1rem 0.3rem; } - .unread-divider { + border-bottom: 1px solid var(--text-primary); + border-radius: 2px; + color: var(--text-primary); + display: flex; + justify-content: center; width: 90%; - color: white; - border-bottom: 1px solid white; - display: flex; - justify-content: center; - border-radius: 2px; } .mention-item { @@ -169,11 +169,10 @@ font-size: 16px; width: 100%; border: none; - color: white; - cursor: pointer; + color: var(--text-primary); &:hover, &:hover.is-selected { - background-color: gray; + background-color: var(--background-default); } } -} \ No newline at end of file +} diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index e0e4692..585b2e0 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -73,6 +73,7 @@ export const CoreSyncStatus = () => { let imagePath = syncingImg; let message = `Synchronizing`; + if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}`; @@ -101,15 +102,17 @@ export const CoreSyncStatus = () => { sync status +

Core Information

@@ -134,7 +137,6 @@ export const CoreSyncStatus = () => { {isUsingGateway?.toString()} -
); diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index 3bcaad5..ebfbc6f 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -22,27 +22,27 @@ export const IconWrapper = ({ return ( {children} {label} @@ -68,18 +68,20 @@ export const DesktopFooter = ({ const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); + const theme = useTheme(); + if (hide) return; return ( @@ -141,8 +143,8 @@ export const DesktopFooter = ({ hasUnreadDirects ? 'var(--danger)' : isDirects - ? 'white' - : 'rgba(250, 250, 250, 0.5)' + ? theme.palette.text.primary + : theme.palette.text.secondary } /> diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index dce577b..d1c8a7a 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -7,6 +7,7 @@ import { useRecoilState } from 'recoil'; import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; +import { CoreSyncStatus } from './CoreSyncStatus'; export const DesktopSideBar = ({ goToHome, @@ -30,19 +31,28 @@ export const DesktopSideBar = ({ return ( + + + + { goToHome(); @@ -51,10 +61,13 @@ export const DesktopSideBar = ({ + { setDesktopViewMode('apps'); @@ -63,17 +76,22 @@ export const DesktopSideBar = ({ }} > + { setDesktopViewMode('chat'); @@ -98,24 +116,7 @@ export const DesktopSideBar = ({ /> - {/* { - setDesktopSideView("groups"); - toggleSideViewGroups() - }} - > - - - */} + {/* */} {isEnabledDevMode && ( @@ -126,7 +127,9 @@ export const DesktopSideBar = ({ > { - +export const DrawerComponent = ({ open, setOpen, children }) => { const toggleDrawer = (newOpen: boolean) => () => { setOpen(newOpen); }; - return (
- - - {children} - + + {children} +
); -} +}; diff --git a/src/components/Drawer/DrawerUserLookup.tsx b/src/components/Drawer/DrawerUserLookup.tsx index f64e096..9f8427a 100644 --- a/src/components/Drawer/DrawerUserLookup.tsx +++ b/src/components/Drawer/DrawerUserLookup.tsx @@ -1,22 +1,26 @@ -import * as React from 'react'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; -export const DrawerUserLookup = ({open, setOpen, children}) => { - +export const DrawerUserLookup = ({ open, setOpen, children }) => { const toggleDrawer = (newOpen: boolean) => () => { setOpen(newOpen); }; - return (
- - - - {children} - + + + {children} +
); -} +}; diff --git a/src/components/Embeds/Embed.tsx b/src/components/Embeds/Embed.tsx index 65b2cef..bef39c6 100644 --- a/src/components/Embeds/Embed.tsx +++ b/src/components/Embeds/Embed.tsx @@ -1,42 +1,46 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { getBaseApiReact } from "../../App"; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { getBaseApiReact } from '../../App'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; +import { extractComponents } from '../Chat/MessageDisplay'; +import { executeEvent } from '../../utils/events'; -import { extractComponents } from "../Chat/MessageDisplay"; -import { executeEvent } from "../../utils/events"; - -import { base64ToBlobUrl } from "../../utils/fileReading"; -import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; -import { blobControllerAtom, blobKeySelector, resourceKeySelector, selectedGroupIdAtom } from "../../atoms/global"; -import { parseQortalLink } from "./embed-utils"; -import { PollCard } from "./PollEmbed"; -import { ImageCard } from "./ImageEmbed"; -import { AttachmentCard } from "./AttachmentEmbed"; -import { decodeIfEncoded } from "../../utils/decode"; +import { base64ToBlobUrl } from '../../utils/fileReading'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { + blobControllerAtom, + blobKeySelector, + resourceKeySelector, + selectedGroupIdAtom, +} from '../../atoms/global'; +import { parseQortalLink } from './embed-utils'; +import { PollCard } from './PollEmbed'; +import { ImageCard } from './ImageEmbed'; +import { AttachmentCard } from './AttachmentEmbed'; +import { decodeIfEncoded } from '../../utils/decode'; const getPoll = async (name) => { const pollName = name; const url = `${getBaseApiReact()}/polls/${pollName}`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); - if (responseData?.message?.includes("POLL_NO_EXISTS")) { - throw new Error("POLL_NO_EXISTS"); + if (responseData?.message?.includes('POLL_NO_EXISTS')) { + throw new Error('POLL_NO_EXISTS'); } else if (responseData?.pollName) { const urlVotes = `${getBaseApiReact()}/polls/votes/${pollName}`; const responseVotes = await fetch(urlVotes, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); @@ -49,56 +53,64 @@ const getPoll = async (name) => { }; export const Embed = ({ embedLink }) => { - const [errorMsg, setErrorMsg] = useState(""); + const [errorMsg, setErrorMsg] = useState(''); const [isLoading, setIsLoading] = useState(false); const [poll, setPoll] = useState(null); - const [type, setType] = useState(""); + const [type, setType] = useState(''); const hasFetched = useRef(false); const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [external, setExternal] = useState(null); - const [imageUrl, setImageUrl] = useState(""); + const [imageUrl, setImageUrl] = useState(''); const [parsedData, setParsedData] = useState(null); const setBlobs = useSetRecoilState(blobControllerAtom); - const [selectedGroupId] = useRecoilState(selectedGroupIdAtom) - const resourceData = useMemo(()=> { + const [selectedGroupId] = useRecoilState(selectedGroupIdAtom); + const resourceData = useMemo(() => { const parsedDataOnTheFly = parseQortalLink(embedLink); - if(parsedDataOnTheFly?.service && parsedDataOnTheFly?.name && parsedDataOnTheFly?.identifier){ + if ( + parsedDataOnTheFly?.service && + parsedDataOnTheFly?.name && + parsedDataOnTheFly?.identifier + ) { return { - service : parsedDataOnTheFly?.service, + service: parsedDataOnTheFly?.service, name: parsedDataOnTheFly?.name, identifier: parsedDataOnTheFly?.identifier, - fileName: parsedDataOnTheFly?.fileName ? decodeURIComponent(parsedDataOnTheFly?.fileName) : null, - mimeType: parsedDataOnTheFly?.mimeType ? decodeURIComponent(parsedDataOnTheFly?.mimeType) : null, - key: parsedDataOnTheFly?.key ? decodeURIComponent(parsedDataOnTheFly?.key) : null, - } + fileName: parsedDataOnTheFly?.fileName + ? decodeURIComponent(parsedDataOnTheFly?.fileName) + : null, + mimeType: parsedDataOnTheFly?.mimeType + ? decodeURIComponent(parsedDataOnTheFly?.mimeType) + : null, + key: parsedDataOnTheFly?.key + ? decodeURIComponent(parsedDataOnTheFly?.key) + : null, + }; } else { - return null + return null; } - }, [embedLink]) + }, [embedLink]); - const keyIdentifier = useMemo(()=> { - - if(resourceData){ - return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}` + const keyIdentifier = useMemo(() => { + if (resourceData) { + return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}`; } else { - return undefined + return undefined; } - }, [resourceData]) + }, [resourceData]); const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier)); const handlePoll = async (parsedData) => { try { setIsLoading(true); - setErrorMsg(""); - setType("POLL"); + setErrorMsg(''); + setType('POLL'); if (!parsedData?.name) - throw new Error("Invalid poll embed link. Missing name."); + throw new Error('Invalid poll embed link. Missing name.'); const pollRes = await getPoll(parsedData.name); setPoll(pollRes); - } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } finally { setIsLoading(false); } @@ -106,8 +118,8 @@ export const Embed = ({ embedLink }) => { const getImage = async ({ identifier, name, service }, key, parsedData) => { try { - if(blobUrl?.blobUrl){ - return blobUrl?.blobUrl + if (blobUrl?.blobUrl) { + return blobUrl?.blobUrl; } let numberOfTries = 0; let imageFinalUrl = null; @@ -116,76 +128,76 @@ export const Embed = ({ embedLink }) => { const urlStatus = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`; const responseStatus = await fetch(urlStatus, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await responseStatus.json(); - if (responseData?.status === "READY") { + if (responseData?.status === 'READY') { if (parsedData?.encryptionType) { const urlData = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; const responseData = await fetch(urlData, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await responseData.text(); if (data) { - let decryptedData + let decryptedData; try { - if(key && encryptionType === 'private'){ + if (key && encryptionType === 'private') { decryptedData = await window.sendMessage( - "DECRYPT_DATA_WITH_SHARING_KEY", - - { - encryptedData: data, + 'DECRYPT_DATA_WITH_SHARING_KEY', + + { + encryptedData: data, key: decodeURIComponent(key), - } - + } ); } - if(encryptionType === 'group'){ - + if (encryptionType === 'group') { decryptedData = await window.sendMessage( - "DECRYPT_QORTAL_GROUP_DATA", - - { - data64: data, - groupId: selectedGroupId, - } - - ); + 'DECRYPT_QORTAL_GROUP_DATA', - } + { + data64: data, + groupId: selectedGroupId, + } + ); + } } catch (error) { - throw new Error('Unable to decrypt') + throw new Error('Unable to decrypt'); } - - if (!decryptedData || decryptedData?.error) throw new Error("Could not decrypt data"); - imageFinalUrl = base64ToBlobUrl(decryptedData, parsedData?.mimeType ? decodeURIComponent(parsedData?.mimeType) : undefined) - setBlobs((prev=> { + + if (!decryptedData || decryptedData?.error) + throw new Error('Could not decrypt data'); + imageFinalUrl = base64ToBlobUrl( + decryptedData, + parsedData?.mimeType + ? decodeURIComponent(parsedData?.mimeType) + : undefined + ); + setBlobs((prev) => { return { ...prev, [`${service}-${name}-${identifier}`]: { blobUrl: imageFinalUrl, - timestamp: Date.now() - } - } - })) + timestamp: Date.now(), + }, + }; + }); } else { - throw new Error('No data for image') + throw new Error('No data for image'); } - } else { - imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`; - - // If parsedData is used here, it must be defined somewhere - - } + imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`; + + // If parsedData is used here, it must be defined somewhere + } } }; @@ -203,18 +215,19 @@ export const Embed = ({ embedLink }) => { } if (imageFinalUrl) { - return imageFinalUrl; } else { setErrorMsg( - "Unable to download IMAGE. Please try again later by clicking the refresh button" + 'Unable to download IMAGE. Please try again later by clicking the refresh button' ); return null; } } catch (error) { - console.error("Error fetching image:", error); + console.error('Error fetching image:', error); setErrorMsg( - error?.error || error?.message || "An unexpected error occurred while trying to download the image" + error?.error || + error?.message || + 'An unexpected error occurred while trying to download the image' ); return null; } @@ -223,25 +236,27 @@ export const Embed = ({ embedLink }) => { const handleImage = async (parsedData) => { try { setIsLoading(true); - setErrorMsg(""); + setErrorMsg(''); if (!parsedData?.name || !parsedData?.service || !parsedData?.identifier) - throw new Error("Invalid image embed link. Missing param."); - let image = await getImage({ - name: parsedData.name, - service: parsedData.service, - identifier: parsedData?.identifier, - }, parsedData?.key, parsedData); - - setImageUrl(image); + throw new Error('Invalid image embed link. Missing param.'); + let image = await getImage( + { + name: parsedData.name, + service: parsedData.service, + identifier: parsedData?.identifier, + }, + parsedData?.key, + parsedData + ); + setImageUrl(image); } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } finally { setIsLoading(false); } }; - const handleLink = () => { try { const parsedData = parseQortalLink(embedLink); @@ -254,28 +269,26 @@ export const Embed = ({ embedLink }) => { setExternal(res); } } - } catch (error) { - - } + } catch (error) {} switch (type) { - case "POLL": + case 'POLL': { handlePoll(parsedData); } break; - case "IMAGE": - setType("IMAGE"); + case 'IMAGE': + setType('IMAGE'); + + break; + case 'ATTACHMENT': + setType('ATTACHMENT'); break; - case "ATTACHMENT": - setType("ATTACHMENT"); - - break; default: break; } } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } }; @@ -284,13 +297,13 @@ export const Embed = ({ embedLink }) => { const parsedData = parseQortalLink(embedLink); handleImage(parsedData); } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } }; const openExternal = () => { - executeEvent("addTab", { data: external }); - executeEvent("open-apps-mode", {}); + executeEvent('addTab', { data: external }); + executeEvent('open-apps-mode', {}); }; useEffect(() => { @@ -299,8 +312,6 @@ export const Embed = ({ embedLink }) => { hasFetched.current = true; }, [embedLink]); - - const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier)); const { parsedType, encryptionType } = useMemo(() => { @@ -312,15 +323,17 @@ export const Embed = ({ embedLink }) => { parsedType = parsedDataOnTheFly.type; } if (parsedDataOnTheFly?.encryptionType) { - encryptionType = parsedDataOnTheFly?.encryptionType + encryptionType = parsedDataOnTheFly?.encryptionType; } - } catch (error) {} + } catch (error) { + console.log(error); + } return { parsedType, encryptionType }; }, [embedLink]); return (
- {parsedType === "POLL" && ( + {parsedType === 'POLL' && ( { errorMsg={errorMsg} /> )} - {parsedType === "IMAGE" && ( + {parsedType === 'IMAGE' && ( { )} {parsedType === 'ATTACHMENT' && ( {
); }; - - - - - - - - diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 3da02c3..65e5983 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -1,5 +1,5 @@ -import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import React, { useContext, useEffect, useState } from 'react'; +import { MyContext } from '../../App'; import { Card, CardContent, @@ -12,384 +12,389 @@ import { Box, ButtonBase, Divider, - -} from "@mui/material"; -import { getNameInfo } from "../Group/Group"; -import PollIcon from "@mui/icons-material/Poll"; -import { getFee } from "../../background"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import { Spacer } from "../../common/Spacer"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import { CustomLoader } from "../../common/CustomLoader"; - +} from '@mui/material'; +import { getNameInfo } from '../Group/Group'; +import PollIcon from '@mui/icons-material/Poll'; +import { getFee } from '../../background'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import { Spacer } from '../../common/Spacer'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { CustomLoader } from '../../common/CustomLoader'; export const PollCard = ({ - poll, - setInfoSnack, - setOpenSnack, - refresh, - openExternal, - external, - isLoadingParent, - errorMsg, - }) => { - const [selectedOption, setSelectedOption] = useState(""); - const [ownerName, setOwnerName] = useState(""); - const [showResults, setShowResults] = useState(false); - const [isOpen, setIsOpen] = useState(false); - const { show, userInfo } = useContext(MyContext); - const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); - const handleVote = async () => { - const fee = await getFee("VOTE_ON_POLL"); - - await show({ - message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`, - publishFee: fee.fee + " QORT", - }); - setIsLoadingSubmit(true); - - window - .sendMessage( - "voteOnPoll", - { - pollName: poll?.info?.pollName, - optionIndex: +selectedOption, - }, - 60000 - ) - .then((response) => { - setIsLoadingSubmit(false); - if (response.error) { - setInfoSnack({ - type: "error", - message: response?.error || "Unable to vote.", - }); - setOpenSnack(true); - return; - } else { - setInfoSnack({ - type: "success", - message: - "Successfully voted. Please wait a couple minutes for the network to propogate the changes.", - }); - setOpenSnack(true); - } - }) - .catch((error) => { - setIsLoadingSubmit(false); + poll, + setInfoSnack, + setOpenSnack, + refresh, + openExternal, + external, + isLoadingParent, + errorMsg, +}) => { + const [selectedOption, setSelectedOption] = useState(''); + const [ownerName, setOwnerName] = useState(''); + const [showResults, setShowResults] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const { show, userInfo } = useContext(MyContext); + const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); + const handleVote = async () => { + const fee = await getFee('VOTE_ON_POLL'); + + await show({ + message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`, + publishFee: fee.fee + ' QORT', + }); + setIsLoadingSubmit(true); + + window + .sendMessage( + 'voteOnPoll', + { + pollName: poll?.info?.pollName, + optionIndex: +selectedOption, + }, + 60000 + ) + .then((response) => { + setIsLoadingSubmit(false); + if (response.error) { setInfoSnack({ - type: "error", - message: error?.message || "Unable to vote.", + type: 'error', + message: response?.error || 'Unable to vote.', + }); + setOpenSnack(true); + return; + } else { + setInfoSnack({ + type: 'success', + message: + 'Successfully voted. Please wait a couple minutes for the network to propogate the changes.', }); setOpenSnack(true); - }); - }; - - const getName = async (owner) => { - try { - const res = await getNameInfo(owner); - if (res) { - setOwnerName(res); } - } catch (error) {} - }; - - useEffect(() => { - if (poll?.info?.owner) { - getName(poll.info.owner); + }) + .catch((error) => { + setIsLoadingSubmit(false); + setInfoSnack({ + type: 'error', + message: error?.message || 'Unable to vote.', + }); + setOpenSnack(true); + }); + }; + + const getName = async (owner) => { + try { + const res = await getNameInfo(owner); + if (res) { + setOwnerName(res); } - }, [poll?.info?.owner]); - - return ( - { + if (poll?.info?.owner) { + getName(poll.info.owner); + } + }, [poll?.info?.owner]); + + return ( + + - - + POLL embed + + + + - POLL embed - - + + {external && ( - - {external && ( - - - - )} - + )} - + + - + + + + {!isOpen && !errorMsg && ( + <> + + + + )} + {isLoadingParent && isOpen && ( + - Created by {ownerName || poll?.info?.owner} -
-
- - - {!isOpen && !errorMsg && ( - <> - - - - )} - {isLoadingParent && isOpen && ( - - {" "} - {" "} - - )} - {errorMsg && ( - - {" "} - - {errorMsg} - {" "} - - )} - - - - {' '} + + )} + {errorMsg && ( + - + > + {' '} - Options - - setSelectedOption(e.target.value)} - > - {poll?.info?.pollOptions?.map((option, index) => ( - - } - label={option?.optionName} - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "14px", - - }, - }} - /> - ))} - - - - {' '} + + )} + + + + + + + Options + + setSelectedOption(e.target.value)} + > + {poll?.info?.pollOptions?.map((option, index) => ( + + } + label={option?.optionName} sx={{ - fontSize: "14px", - fontStyle: "italic", + '& .MuiFormControlLabel-label': { + fontSize: '14px', + }, + }} + /> + ))} + + + + + {' '} + {`${poll?.votes?.totalVotes} ${ + poll?.votes?.totalVotes === 1 ? ' vote' : ' votes' + }`} + + + + + item?.voterPublicKey === userInfo?.publicKey + ) + ? 'visible' + : 'hidden', + }} + > + You've already voted. + + + {isLoadingSubmit && ( + + Is processing transaction, please wait... + + )} + { + setShowResults((prev) => !prev); + }} + > + {showResults ? 'hide ' : 'show '} results + + + {showResults && } + + + ); +}; + +const PollResults = ({ votes }) => { + const maxVotes = Math.max( + ...votes?.voteCounts?.map((option) => option.voteCount) + ); + const options = votes?.voteCounts; + return ( + + {options + .sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first) + .map((option, index) => ( + + + - {" "} - {`${poll?.votes?.totalVotes} ${ - poll?.votes?.totalVotes === 1 ? " vote" : " votes" - }`} + {`${index + 1}. ${option.optionName}`} + + + {option.voteCount} votes - - - item?.voterPublicKey === userInfo?.publicKey - ) - ? "visible" - : "hidden", + mt: 1, + height: 10, + backgroundColor: '#e0e0e0', + borderRadius: 5, + overflow: 'hidden', }} > - You've already voted. - - - {isLoadingSubmit && ( - - Is processing transaction, please wait... - - )} - { - setShowResults((prev) => !prev); - }} - > - {showResults ? "hide " : "show "} results - - - {showResults && } - - - ); - }; - - const PollResults = ({ votes }) => { - const maxVotes = Math.max( - ...votes?.voteCounts?.map((option) => option.voteCount) - ); - const options = votes?.voteCounts; - return ( - - {options - .sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first) - .map((option, index) => ( - - - - {`${index + 1}. ${option.optionName}`} - - - {option.voteCount} votes - - - - + /> - ))} - - ); - }; \ No newline at end of file + + ))} + + ); +}; diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 1cf86fe..5be818b 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -1,19 +1,15 @@ -import * as React from "react"; -import Button from "@mui/material/Button"; -import Dialog from "@mui/material/Dialog"; -import ListItemText from "@mui/material/ListItemText"; -import ListItemButton from "@mui/material/ListItemButton"; -import List from "@mui/material/List"; -import Divider from "@mui/material/Divider"; -import AppBar from "@mui/material/AppBar"; -import Toolbar from "@mui/material/Toolbar"; -import IconButton from "@mui/material/IconButton"; -import Typography from "@mui/material/Typography"; -import CloseIcon from "@mui/icons-material/Close"; -import ExpandLess from "@mui/icons-material/ExpandLess"; -import ExpandMore from "@mui/icons-material/ExpandMore"; -import Slide from "@mui/material/Slide"; -import { TransitionProps } from "@mui/material/transitions"; +import * as React from 'react'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import CloseIcon from '@mui/icons-material/Close'; +import ExpandLess from '@mui/icons-material/ExpandLess'; +import ExpandMore from '@mui/icons-material/ExpandMore'; +import Slide from '@mui/material/Slide'; +import { TransitionProps } from '@mui/material/transitions'; import { Box, Collapse, @@ -24,23 +20,23 @@ import { Tab, Tabs, styled, -} from "@mui/material"; -import { AddGroupList } from "./AddGroupList"; -import { UserListOfInvites } from "./UserListOfInvites"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { getFee } from "../../background"; -import { MyContext, isMobile } from "../../App"; -import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; + useTheme, +} from '@mui/material'; +import { AddGroupList } from './AddGroupList'; +import { UserListOfInvites } from './UserListOfInvites'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { getFee } from '../../background'; +import { MyContext, isMobile } from '../../App'; +import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; -export const Label = styled("label")( - ({ theme }) => ` +export const Label = styled('label')` + display: block; font-family: 'IBM Plex Sans', sans-serif; font-size: 14px; - display: block; - margin-bottom: 4px; font-weight: 400; - ` -); + margin-bottom: 4px; +`; + const Transition = React.forwardRef(function Transition( props: TransitionProps & { children: React.ReactElement; @@ -51,17 +47,15 @@ const Transition = React.forwardRef(function Transition( }); export const AddGroup = ({ address, open, setOpen }) => { - const {show, setTxList} = React.useContext(MyContext) - - const [tab, setTab] = React.useState("create"); + const { show, setTxList } = React.useContext(MyContext); + const [tab, setTab] = React.useState('create'); const [openAdvance, setOpenAdvance] = React.useState(false); - - const [name, setName] = React.useState(""); - const [description, setDescription] = React.useState(""); - const [groupType, setGroupType] = React.useState("1"); - const [approvalThreshold, setApprovalThreshold] = React.useState("40"); - const [minBlock, setMinBlock] = React.useState("5"); - const [maxBlock, setMaxBlock] = React.useState("21600"); + const [name, setName] = React.useState(''); + const [description, setDescription] = React.useState(''); + const [groupType, setGroupType] = React.useState('1'); + const [approvalThreshold, setApprovalThreshold] = React.useState('40'); + const [minBlock, setMinBlock] = React.useState('5'); + const [maxBlock, setMaxBlock] = React.useState('21600'); const [value, setValue] = React.useState(0); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); @@ -69,6 +63,7 @@ export const AddGroup = ({ address, open, setOpen }) => { const handleChange = (event: React.SyntheticEvent, newValue: number) => { setValue(newValue); }; + const handleClose = () => { setOpen(false); }; @@ -89,58 +84,59 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; - + const theme = useTheme(); const handleCreateGroup = async () => { try { - if(!name) throw new Error('Please provide a name') - if(!description) throw new Error('Please provide a description') + if (!name) throw new Error('Please provide a name'); + if (!description) throw new Error('Please provide a description'); - const fee = await getFee('CREATE_GROUP') + const fee = await getFee('CREATE_GROUP'); await show({ - message: "Would you like to perform an CREATE_GROUP transaction?" , - publishFee: fee.fee + ' QORT' - }) + message: 'Would you like to perform an CREATE_GROUP transaction?', + publishFee: fee.fee + ' QORT', + }); - await new Promise((res, rej) => { - window.sendMessage("createGroup", { - groupName: name, - groupDescription: description, - groupType: +groupType, - groupApprovalThreshold: +approvalThreshold, - minBlock: +minBlock, - maxBlock: +maxBlock, - }) - .then((response) => { - if (!response?.error) { - setInfoSnack({ - type: "success", - message: "Successfully created group. It may take a couple of minutes for the changes to propagate", - }); - setOpenSnack(true); - setTxList((prev) => [ - { - ...response, - type: 'created-group', - label: `Created group ${name}: awaiting confirmation`, - labelDone: `Created group ${name}: success!`, - done: false, - }, - ...prev, - ]); - res(response); - return; - } - rej({ message: response.error }); - }) - .catch((error) => { - rej({ message: error.message || "An error occurred" }); - }); - + await new Promise((res, rej) => { + window + .sendMessage('createGroup', { + groupName: name, + groupDescription: description, + groupType: +groupType, + groupApprovalThreshold: +approvalThreshold, + minBlock: +minBlock, + maxBlock: +maxBlock, + }) + .then((response) => { + if (!response?.error) { + setInfoSnack({ + type: 'success', + message: + 'Successfully created group. It may take a couple of minutes for the changes to propagate', + }); + setOpenSnack(true); + setTxList((prev) => [ + { + ...response, + type: 'created-group', + label: `Created group ${name}: awaiting confirmation`, + labelDone: `Created group ${name}: success!`, + done: false, + }, + ...prev, + ]); + res(response); + return; + } + rej({ message: response.error }); + }) + .catch((error) => { + rej({ message: error.message || 'An error occurred' }); + }); }); } catch (error) { setInfoSnack({ - type: "error", + type: 'error', message: error?.message, }); setOpenSnack(true); @@ -166,20 +162,22 @@ export const AddGroup = ({ address, open, setOpen }) => { function a11yProps(index: number) { return { id: `simple-tab-${index}`, - "aria-controls": `simple-tabpanel-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, }; } - - const openGroupInvitesRequestFunc = ()=> { - setValue(2) - } + const openGroupInvitesRequestFunc = () => { + setValue(2); + }; React.useEffect(() => { - subscribeToEvent("openGroupInvitesRequest", openGroupInvitesRequestFunc); + subscribeToEvent('openGroupInvitesRequest', openGroupInvitesRequestFunc); return () => { - unsubscribeFromEvent("openGroupInvitesRequest", openGroupInvitesRequestFunc); + unsubscribeFromEvent( + 'openGroupInvitesRequest', + openGroupInvitesRequestFunc + ); }; }, []); @@ -191,17 +189,22 @@ export const AddGroup = ({ address, open, setOpen }) => { onClose={handleClose} TransitionComponent={Transition} > - + - - Group Mgmt + + Group Management @@ -213,275 +216,292 @@ export const AddGroup = ({ address, open, setOpen }) => { - - - - - - + + + + + + - + {value === 0 && ( - - - setName(e.target.value)} - /> - - - - - setDescription(e.target.value)} - /> - - - - - - setOpenAdvance((prev) => !prev)} - > - Advanced options - - {openAdvance ? : } - - - + + setName(e.target.value)} + /> + + + + + setDescription(e.target.value)} + /> + + + setOpenAdvance((prev) => !prev)} > - - + Advanced options + + {openAdvance ? : } + + + + + + + + + + + + + + - - + Create Group + - - - - )} {value === 1 && ( - - - + + - )} - - {value === 2 && ( - - - - )} - + {value === 2 && ( + + + + )} - + + ); diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index ccd4850..433d0e4 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -7,7 +7,7 @@ import { Popover, TextField, Typography, -} from "@mui/material"; +} from '@mui/material'; import React, { useCallback, useContext, @@ -15,20 +15,20 @@ import React, { useMemo, useRef, useState, -} from "react"; +} from 'react'; import { AutoSizer, CellMeasurer, CellMeasurerCache, List, -} from "react-virtualized"; -import _ from "lodash"; -import { MyContext, getBaseApiReact } from "../../App"; -import { LoadingButton } from "@mui/lab"; -import { getBaseApi, getFee } from "../../background"; +} from 'react-virtualized'; +import _ from 'lodash'; +import { MyContext, getBaseApiReact } from '../../App'; +import { LoadingButton } from '@mui/lab'; +import { getBaseApi, getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; -import { Spacer } from "../../common/Spacer"; +import { Spacer } from '../../common/Spacer'; const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, @@ -41,7 +41,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(); - const [inputValue, setInputValue] = useState(""); + const [inputValue, setInputValue] = useState(''); const [filteredItems, setFilteredItems] = useState(groups); const [isLoading, setIsLoading] = useState(false); @@ -72,9 +72,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const getGroups = async () => { try { - const response = await fetch( - `${getBaseApiReact()}/groups/?limit=0` - ); + const response = await fetch(`${getBaseApiReact()}/groups/?limit=0`); const groupData = await response.json(); const filteredGroup = groupData.filter( (item) => !memberGroups.find((group) => group.groupId === item.groupId) @@ -103,23 +101,25 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP') - await show({ - message: "Would you like to perform an JOIN_GROUP transaction?" , - publishFee: fee.fee + ' QORT' - }) + const fee = await getFee('JOIN_GROUP'); + await show({ + message: 'Would you like to perform an JOIN_GROUP transaction?', + publishFee: fee.fee + ' QORT', + }); setIsLoading(true); await new Promise((res, rej) => { - window.sendMessage("joinGroup", { - groupId, - }) + window + .sendMessage('joinGroup', { + groupId, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', }); - + if (isOpen) { setTxList((prev) => [ { @@ -145,14 +145,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { ...prev, ]); } - + setOpenSnack(true); handlePopoverClose(); res(response); return; } else { setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -161,18 +161,18 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); setIsLoading(false); - } catch (error) {} finally { + } catch (error) { + console.log(error); + } finally { setIsLoading(false); - } }; @@ -195,30 +195,30 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { anchorEl={popoverAnchor} onClose={handlePopoverClose} anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > Join {group?.groupName} {group?.isOpen === false && - "This is a closed/private group, so you will need to wait until an admin accepts your request"} + 'This is a closed/private group, so you will need to wait until an admin accepts your request'} { onClick={(event) => handlePopoverOpen(event, index)} > {group?.isOpen === false && ( - - )} - {group?.isOpen === true && ( - - )} - + + )} + {group?.isOpen === true && ( + + )} + { }; return ( - +

Groups list

{ />
diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 24b3f01..b2d58e7 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -8,64 +8,78 @@ import { DialogTitle, TextField, Typography, -} from "@mui/material"; -import React, { useContext, useEffect, useState } from "react"; -import { getBaseApiReact, MyContext } from "../../App"; -import { Spacer } from "../../common/Spacer"; -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; -import { validateAddress } from "../../utils/validateAddress"; -import { getNameInfo, requestQueueMemberNames } from "./Group"; -import { useModal } from "../../common/useModal"; -import { useRecoilState } from "recoil"; -import { isOpenBlockedModalAtom } from "../../atoms/global"; + useTheme, +} from '@mui/material'; +import { useContext, useEffect, useState } from 'react'; +import { getBaseApiReact, MyContext } from '../../App'; +import { Spacer } from '../../common/Spacer'; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from '../../utils/events'; +import { validateAddress } from '../../utils/validateAddress'; +import { getNameInfo, requestQueueMemberNames } from './Group'; +import { useModal } from '../../common/useModal'; +import { useRecoilState } from 'recoil'; +import { isOpenBlockedModalAtom } from '../../atoms/global'; import InfoIcon from '@mui/icons-material/Info'; + export const BlockedUsersModal = () => { - const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom) + const theme = useTheme(); + const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState( + isOpenBlockedModalAtom + ); const [hasChanged, setHasChanged] = useState(false); - const [value, setValue] = useState(""); - const [addressesWithNames, setAddressesWithNames] = useState({}) + const [value, setValue] = useState(''); + const [addressesWithNames, setAddressesWithNames] = useState({}); const { isShow, onCancel, onOk, show, message } = useModal(); - const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } = - useContext(MyContext); + const { + getAllBlockedUsers, + removeBlockFromList, + addToBlockList, + setOpenSnackGlobal, + setInfoSnackCustom, + } = useContext(MyContext); + const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, names: {}, }); + const fetchBlockedUsers = () => { setBlockedUsers(getAllBlockedUsers()); }; useEffect(() => { - if(!isOpenBlockedModal) return + if (!isOpenBlockedModal) return; fetchBlockedUsers(); }, [isOpenBlockedModal]); - const getNames = async () => { + const getNames = async () => { // const validApi = await findUsableApi(); - const addresses = Object.keys(blockedUsers?.addresses) - const addressNames = {} + const addresses = Object.keys(blockedUsers?.addresses); + const addressNames = {}; - const getMemNames = addresses.map(async (address) => { - const name = await requestQueueMemberNames.enqueue(() => { - return getNameInfo(address); - }); - if (name) { - addressNames[address] = name - } - - + const name = await requestQueueMemberNames.enqueue(() => { + return getNameInfo(address); + }); + if (name) { + addressNames[address] = name; + } + return true; }); - + await Promise.all(getMemNames); - - setAddressesWithNames(addressNames) + + setAddressesWithNames(addressNames); }; const blockUser = async (e, user?: string) => { try { - const valUser = user || value + const valUser = user || value; if (!valUser) return; const isAddress = validateAddress(valUser); let userName = null; @@ -80,62 +94,66 @@ export const BlockedUsersModal = () => { if (!isAddress) { const response = await fetch(`${getBaseApiReact()}/names/${valUser}`); const data = await response.json(); - if (!data?.owner) throw new Error("Name does not exist"); + if (!data?.owner) throw new Error('Name does not exist'); if (data?.owner) { userAddress = data.owner; userName = valUser; } } - if(!userName){ + if (!userName) { await addToBlockList(userAddress, null); fetchBlockedUsers(); setHasChanged(true); - executeEvent('updateChatMessagesWithBlocks', true) - setValue('') - return + executeEvent('updateChatMessagesWithBlocks', true); + setValue(''); + return; } const responseModal = await show({ userName, userAddress, }); - if (responseModal === "both") { + if (responseModal === 'both') { await addToBlockList(userAddress, userName); - } else if (responseModal === "address") { + } else if (responseModal === 'address') { await addToBlockList(userAddress, null); - } else if (responseModal === "name") { + } else if (responseModal === 'name') { await addToBlockList(null, userName); } fetchBlockedUsers(); setHasChanged(true); - setValue('') - if(user){ - setIsOpenBlockedModal(false) + setValue(''); + if (user) { + setIsOpenBlockedModal(false); } - if(responseModal === 'both' || responseModal === 'address'){ - executeEvent('updateChatMessagesWithBlocks', true) + if (responseModal === 'both' || responseModal === 'address') { + executeEvent('updateChatMessagesWithBlocks', true); } } catch (error) { setOpenSnackGlobal(true); - - setInfoSnackCustom({ - type: "error", - message: error?.message || "Unable to block user", - }); + setInfoSnackCustom({ + type: 'error', + message: error?.message || 'Unable to block user', + }); } }; + const blockUserFromOutsideModalFunc = (e) => { - const user = e.detail?.user; - setIsOpenBlockedModal(true) - blockUser(null, user) + const user = e.detail?.user; + setIsOpenBlockedModal(true); + blockUser(null, user); + }; + + useEffect(() => { + subscribeToEvent('blockUserFromOutside', blockUserFromOutsideModalFunc); + + return () => { + unsubscribeFromEvent( + 'blockUserFromOutside', + blockUserFromOutsideModalFunc + ); }; - - useEffect(() => { - subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); - - return () => { - unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); - }; - }, []); + }, []); + return ( { Blocked Users { Blocked addresses- blocks processing of txs - + )} {Object.entries(blockedUsers?.addresses || {})?.map( @@ -197,11 +217,11 @@ export const BlockedUsersModal = () => { return ( {addressesWithNames[key] || key} @@ -215,7 +235,7 @@ export const BlockedUsersModal = () => { try { await removeBlockFromList(key, undefined); setHasChanged(true); - setValue(""); + setValue(''); fetchBlockedUsers(); } catch (error) { console.error(error); @@ -241,20 +261,20 @@ export const BlockedUsersModal = () => { {Object.entries(blockedUsers?.names || {})?.map(([key, value]) => { return ( {key} @@ -284,20 +304,20 @@ export const BlockedUsersModal = () => { + )} - {value === 1 && ( + {value === 1 && ( - + )} {value === 2 && ( - - + )} {value === 3 && ( - + )} - + {value === 4 && ( - + )} - + - ); }; diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index c2d7370..6b87f1e 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -1,14 +1,12 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react' -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import moment from 'moment' -import { Box, ButtonBase, Collapse, Typography } from "@mui/material"; -import { Spacer } from "../../common/Spacer"; -import { getBaseApiReact, isMobile } from "../../App"; -import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import moment from 'moment'; +import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material'; +import { getBaseApiReact, isMobile } from '../../App'; import MailIcon from '@mui/icons-material/Mail'; import MailOutlineIcon from '@mui/icons-material/MailOutline'; import { executeEvent } from '../../utils/events'; @@ -18,262 +16,283 @@ import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread'; + export const isLessThanOneWeekOld = (timestamp) => { // Current time in milliseconds const now = Date.now(); - + // One week ago in milliseconds (7 days * 24 hours * 60 minutes * 60 seconds * 1000 milliseconds) - const oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000); - + const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000; + // Check if the timestamp is newer than one week ago return timestamp > oneWeekAgo; }; + export function formatEmailDate(timestamp: number) { - const date = moment(timestamp); - const now = moment(); + const date = moment(timestamp); + const now = moment(); - if (date.isSame(now, 'day')) { - // If the email was received today, show the time - return date.format('h:mm A'); - } else if (date.isSame(now, 'year')) { - // If the email was received this year, show the month and day - return date.format('MMM D'); - } else { - // For older emails, show the full date - return date.format('MMM D, YYYY'); - } + if (date.isSame(now, 'day')) { + // If the email was received today, show the time + return date.format('h:mm A'); + } else if (date.isSame(now, 'year')) { + // If the email was received this year, show the month and day + return date.format('MMM D'); + } else { + // For older emails, show the full date + return date.format('MMM D, YYYY'); + } } -export const QMailMessages = ({userName, userAddress}) => { - const [isExpanded, setIsExpanded] = useState(false) - const [mails, setMails] = useRecoilState(mailsAtom) - const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom) - const [loading, setLoading] = useState(true) - const getMails = useCallback(async () => { - try { - setLoading(true) - const query = `qortal_qmail_${userName.slice( - 0, - 20 - )}_${userAddress.slice(-6)}_mail_` - const response = await fetch(`${getBaseApiReact()}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true&mode=ALL`); - const mailData = await response.json(); - - - setMails(mailData); - } catch (error) { - console.error(error); - } finally { - setLoading(false) +export const QMailMessages = ({ userName, userAddress }) => { + const [isExpanded, setIsExpanded] = useState(false); + const [mails, setMails] = useRecoilState(mailsAtom); + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( + qMailLastEnteredTimestampAtom + ); + const [loading, setLoading] = useState(true); + const theme = useTheme(); - } - }, []) + const getMails = useCallback(async () => { + try { + setLoading(true); + const query = `qortal_qmail_${userName.slice( + 0, + 20 + )}_${userAddress.slice(-6)}_mail_`; + const response = await fetch( + `${getBaseApiReact()}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true&mode=ALL` + ); + const mailData = await response.json(); - const getTimestamp = async () => { - try { - return new Promise((res, rej) => { - window.sendMessage("getEnteredQmailTimestamp") - .then((response) => { - if (!response?.error) { - if(response?.timestamp){ - setLastEnteredTimestamp(response?.timestamp) - } + setMails(mailData); + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } + }, []); + + const getTimestamp = async () => { + try { + return new Promise((res, rej) => { + window + .sendMessage('getEnteredQmailTimestamp') + .then((response) => { + if (!response?.error) { + if (response?.timestamp) { + setLastEnteredTimestamp(response?.timestamp); } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); }); - } catch (error) {} - }; - - useEffect(() => { - getTimestamp() - if(!userName || !userAddress) return - getMails(); + }); + } catch (error) { + console.log(error); + } + }; - const interval = setInterval(() => { - getTimestamp() - getMails(); - }, 300000); - - return () => clearInterval(interval); - - }, [getMails, userName, userAddress]); + useEffect(() => { + getTimestamp(); + if (!userName || !userAddress) return; + getMails(); - const anyUnread = useMemo(()=> { - let unread = false - - mails.forEach((mail)=> { - if(!lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) || (lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) && lastEnteredTimestamp < mail?.created)){ - unread = true - } - }) - return unread - }, [mails, lastEnteredTimestamp]) + const interval = setInterval(() => { + getTimestamp(); + getMails(); + }, 300000); + + return () => clearInterval(interval); + }, [getMails, userName, userAddress]); + + const anyUnread = useMemo(() => { + let unread = false; + + mails.forEach((mail) => { + if ( + (!lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created)) || + (lastEnteredTimestamp && + isLessThanOneWeekOld(mail?.created) && + lastEnteredTimestamp < mail?.created) + ) { + unread = true; + } + }); + return unread; + }, [mails, lastEnteredTimestamp]); return ( - - setIsExpanded((prev)=> !prev)} > - setIsExpanded((prev) => !prev)} > - Latest Q-Mails - - - {isExpanded ? : ( - - )} - - - - {loading && mails.length === 0 && ( - + Latest Q-Mails + + + {isExpanded ? ( + - - + /> + ) : ( + )} - {!loading && mails.length === 0 && ( - - + + + + {loading && mails.length === 0 && ( + - Nothing to display - - - )} - - - {mails?.map((mail)=> { - return ( - { - executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } }); - executeEvent("open-apps-mode", { }); - setLastEnteredTimestamp(Date.now()) + + + )} + {!loading && mails.length === 0 && ( + + + Nothing to display + + + )} + + {mails?.map((mail) => { + return ( + { + executeEvent('addTab', { + data: { service: 'APP', name: 'q-mail' }, + }); + executeEvent('open-apps-mode', {}); + setLastEnteredTimestamp(Date.now()); + }} + > + - + - - - {!lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) ? ( - - ) : !lastEnteredTimestamp ? ( - - ): (lastEnteredTimestamp < mail?.created) && isLessThanOneWeekOld(mail?.created) ? ( - - ) : ( - - ) - } - - - - - - - ) + {!lastEnteredTimestamp && + isLessThanOneWeekOld(mail?.created) ? ( + + ) : !lastEnteredTimestamp ? ( + + ) : lastEnteredTimestamp < mail?.created && + isLessThanOneWeekOld(mail?.created) ? ( + + ) : ( + + )} + + + + ); })} - - - - - + + + - - - ) -} + ); +}; diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 2aba6f6..d3ff1f7 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -1,37 +1,20 @@ -import * as React from "react"; -import Button from "@mui/material/Button"; -import Dialog from "@mui/material/Dialog"; -import ListItemText from "@mui/material/ListItemText"; -import ListItemButton from "@mui/material/ListItemButton"; -import List from "@mui/material/List"; -import Divider from "@mui/material/Divider"; -import AppBar from "@mui/material/AppBar"; -import Toolbar from "@mui/material/Toolbar"; -import IconButton from "@mui/material/IconButton"; -import Typography from "@mui/material/Typography"; -import CloseIcon from "@mui/icons-material/Close"; -import Slide from "@mui/material/Slide"; -import { TransitionProps } from "@mui/material/transitions"; -import ListOfMembers from "./ListOfMembers"; -import { InviteMember } from "./InviteMember"; -import { ListOfInvites } from "./ListOfInvites"; -import { ListOfBans } from "./ListOfBans"; -import { ListOfJoinRequests } from "./ListOfJoinRequests"; -import { Box, FormControlLabel, Switch, Tab, Tabs, styled } from "@mui/material"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { MyContext, isMobile } from "../../App"; -import { getGroupMembers, getNames } from "./Group"; -import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; -import { getFee } from "../../background"; -import { LoadingButton } from "@mui/lab"; -import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; -import { enabledDevModeAtom } from "../../atoms/global"; -import { useRecoilState } from "recoil"; +import * as React from 'react'; +import Dialog from '@mui/material/Dialog'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import CloseIcon from '@mui/icons-material/Close'; +import Slide from '@mui/material/Slide'; +import { TransitionProps } from '@mui/material/transitions'; +import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; +import { enabledDevModeAtom } from '../../atoms/global'; +import { useRecoilState } from 'recoil'; function a11yProps(index: number) { return { id: `simple-tab-${index}`, - "aria-controls": `simple-tabpanel-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, }; } @@ -49,13 +32,13 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, '&::before': { backgroundImage: `url('data:image/svg+xml;utf8,')`, left: 12, }, '&::after': { backgroundImage: `url('data:image/svg+xml;utf8,')`, right: 12, }, @@ -77,35 +60,34 @@ const Transition = React.forwardRef(function Transition( return ; }); -export const Settings = ({ - address, - open, - setOpen, -}) => { +export const Settings = ({ address, open, setOpen }) => { const [checked, setChecked] = React.useState(false); - const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom) - - + const [isEnabledDevMode, setIsEnabledDevMode] = + useRecoilState(enabledDevModeAtom); + const theme = useTheme(); const handleChange = (event: React.ChangeEvent) => { setChecked(event.target.checked); - window.sendMessage("addUserSettings", { - keyValue: { - key: 'disable-push-notifications', - value: event.target.checked, - }, - }) + window + .sendMessage('addUserSettings', { + keyValue: { + key: 'disable-push-notifications', + value: event.target.checked, + }, + }) .then((response) => { if (response?.error) { - console.error("Error adding user settings:", response.error); + console.error('Error adding user settings:', response.error); } else { - console.log("User settings added successfully"); + console.log('User settings added successfully'); } }) .catch((error) => { - console.error("Failed to add user settings:", error.message || "An error occurred"); + console.error( + 'Failed to add user settings:', + error.message || 'An error occurred' + ); }); - }; const handleClose = () => { @@ -115,9 +97,10 @@ export const Settings = ({ const getUserSettings = async () => { try { return new Promise((res, rej) => { - window.sendMessage("getUserSettings", { - key: "disable-push-notifications", - }) + window + .sendMessage('getUserSettings', { + key: 'disable-push-notifications', + }) .then((response) => { if (!response?.error) { setChecked(response || false); @@ -127,12 +110,11 @@ export const Settings = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) { - console.log("error", error); + console.log('error', error); } }; @@ -140,8 +122,6 @@ export const Settings = ({ getUserSettings(); }, []); - - return ( - + - + General Settings + + @@ -188,17 +172,23 @@ export const Settings = ({ /> {window?.electronAPI && ( { - setIsEnabledDevMode(e.target.checked) - localStorage.setItem('isEnabledDevMode', JSON.stringify(e.target.checked)) - }} /> - } - label="Enable dev mode" - /> + sx={{ + color: 'white', + }} + control={ + { + setIsEnabledDevMode(e.target.checked); + localStorage.setItem( + 'isEnabledDevMode', + JSON.stringify(e.target.checked) + ); + }} + /> + } + label="Enable dev mode" + /> )} diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 788a36a..c569425 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -1,23 +1,17 @@ -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 { Box, ButtonBase, Divider, Typography } from '@mui/material'; +import { 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"; +} from '../../utils/events'; +import { useRecoilState } from 'recoil'; +import { navigationControllerAtom } from '../../atoms/global'; +import { AppsNavBarLeft, AppsNavBarParent } from '../Apps/Apps-styles'; +import { NavBack } from '../../assets/Icons/NavBack.tsx'; +import RefreshIcon from '@mui/icons-material/Refresh'; export const WalletsAppWrapper = () => { const iframeRef = useRef(null); @@ -26,10 +20,10 @@ export const WalletsAppWrapper = () => { navigationControllerAtom ); const [selectedTab, setSelectedTab] = useState({ - tabId: "5558589", - name: "Q-Wallets", - service: "APP", - path: 'qortal?authOnMount=true' + tabId: '5558589', + name: 'Q-Wallets', + service: 'APP', + path: 'qortal?authOnMount=true', }); const isDisableBackButton = useMemo(() => { @@ -48,64 +42,64 @@ export const WalletsAppWrapper = () => { ); useEffect(() => { - subscribeToEvent("openWalletsApp", openWalletsAppFunc); + subscribeToEvent('openWalletsApp', openWalletsAppFunc); return () => { - unsubscribeFromEvent("openWalletsApp", openWalletsAppFunc); + unsubscribeFromEvent('openWalletsApp', openWalletsAppFunc); }; }, [openWalletsAppFunc]); - const handleClose = ()=> { + const handleClose = () => { setIsOpen(false); - iframeRef.current = null - } + iframeRef.current = null; + }; return ( <> {isOpen && ( Q-Wallets - + + + { skipAuth={true} /> - + { executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {}); @@ -124,31 +120,30 @@ export const WalletsAppWrapper = () => { disabled={isDisableBackButton} sx={{ opacity: !isDisableBackButton ? 1 : 0.1, - cursor: !isDisableBackButton ? "pointer" : "default", + cursor: !isDisableBackButton ? 'pointer' : 'default', }} > - + - { - if (selectedTab?.refreshFunc) { - selectedTab.refreshFunc(selectedTab?.tabId); - - } else { - executeEvent("refreshApp", { - tabId: selectedTab?.tabId, - }); - } - - - }}> - + { + if (selectedTab?.refreshFunc) { + selectedTab.refreshFunc(selectedTab?.tabId); + } else { + executeEvent('refreshApp', { + tabId: selectedTab?.tabId, + }); + } + }} + > + diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index faca10a..ed0a119 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -1,22 +1,29 @@ -import React, { useContext, useEffect, useState } from "react"; -import Logo2 from "../assets/svgs/Logo2.svg"; -import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from "../App"; -import { Avatar, Box, Button, ButtonBase, Popover, Typography } from "@mui/material"; -import { Spacer } from "../common/Spacer"; -import ImageUploader from "../common/ImageUploader"; -import { getFee } from "../background"; -import { fileToBase64 } from "../utils/fileReading"; -import { LoadingButton } from "@mui/lab"; +import React, { useContext, useEffect, useState } from 'react'; +import Logo2 from '../assets/svgs/Logo2.svg'; +import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; +import { + Avatar, + Box, + Button, + ButtonBase, + Popover, + Typography, +} from '@mui/material'; +import { Spacer } from '../common/Spacer'; +import ImageUploader from '../common/ImageUploader'; +import { getFee } from '../background'; +import { fileToBase64 } from '../utils/fileReading'; +import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const [hasAvatar, setHasAvatar] = useState(false); const [avatarFile, setAvatarFile] = useState(null); - const [tempAvatar, setTempAvatar] = useState(null) + const [tempAvatar, setTempAvatar] = useState(null); const { show } = useContext(MyContext); const [anchorEl, setAnchorEl] = useState(null); -const [isLoading, setIsLoading] = useState(false) + const [isLoading, setIsLoading] = useState(false); // Handle child element click to open Popover const handleChildClick = (event) => { event.stopPropagation(); // Prevent parent onClick from firing @@ -37,93 +44,105 @@ const [isLoading, setIsLoading] = useState(false) const identifier = `qortal_avatar`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=THUMBNAIL&identifier=${identifier}&limit=1&name=${myName}&includemetadata=false&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); if (responseData?.length > 0) { setHasAvatar(true); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; useEffect(() => { if (!myName) return; checkIfAvatarExists(); }, [myName]); - - const publishAvatar = async ()=> { + const publishAvatar = async () => { try { - const fee = await getFee('ARBITRARY') - if(+balance < +fee.fee) throw new Error(`Publishing an Avatar requires ${fee.fee}`) - await show({ - message: "Would you like to publish an avatar?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoading(true); - const avatarBase64 = await fileToBase64(avatarFile) - await new Promise((res, rej) => { - window.sendMessage("publishOnQDN", { - data: avatarBase64, - identifier: "qortal_avatar", - service: "THUMBNAIL", - }) - .then((response) => { - if (!response?.error) { - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }); - setAvatarFile(null); - setTempAvatar(`data:image/webp;base64,${avatarBase64}`) - handleClose() + const fee = await getFee('ARBITRARY'); + if (+balance < +fee.fee) + throw new Error(`Publishing an Avatar requires ${fee.fee}`); + await show({ + message: 'Would you like to publish an avatar?', + publishFee: fee.fee + ' QORT', + }); + setIsLoading(true); + const avatarBase64 = await fileToBase64(avatarFile); + await new Promise((res, rej) => { + window + .sendMessage('publishOnQDN', { + data: avatarBase64, + identifier: 'qortal_avatar', + service: 'THUMBNAIL', + }) + .then((response) => { + if (!response?.error) { + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); + setAvatarFile(null); + setTempAvatar(`data:image/webp;base64,${avatarBase64}`); + handleClose(); } catch (error) { if (error?.message) { - setOpenSnack(true) - setInfoSnack({ - type: "error", - message: error?.message, - }); - } + setOpenSnack(true); + setInfoSnack({ + type: 'error', + message: error?.message, + }); + } } finally { - setIsLoading(false); + setIsLoading(false); } - } + }; - if(tempAvatar){ + if (tempAvatar) { return ( - <> - + + {myName?.charAt(0)} + + + - {myName?.charAt(0)} - - - - change avatar - - - - - ); + change avatar + + + + + ); } if (hasAvatar) { @@ -131,8 +150,8 @@ const [isLoading, setIsLoading] = useState(false) <> change avatar - + ); } @@ -160,42 +189,61 @@ const [isLoading, setIsLoading] = useState(false) set avatar - + ); }; - -const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose, publishAvatar, isLoading, myName}) => { - return ( - { + return ( + - + - (500 KB max. for GIFS){" "} + (500 KB max. for GIFS){' '} setAvatarFile(file)}> @@ -203,23 +251,34 @@ const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose {avatarFile?.name} {!myName && ( - - - A registered name is required to set an avatar + alignItems: 'center', + }} + > + + + A registered name is required to set an avatar + )} - - - + + + Publish avatar - ) - }; \ No newline at end of file + ); +}; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 4a9eeb3..84697c8 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -9,31 +9,20 @@ import { DialogTitle, Divider, IconButton, - InputBase, - InputLabel, Snackbar, Typography, + useTheme, } from '@mui/material'; -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import CloseIcon from '@mui/icons-material/Close'; -import { MyContext, getBaseApiReact } from '../../App'; +import { getBaseApiReact } from '../../App'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; import { getFee, getNameOrAddress } from '../../background'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import { AddressBox } from '../../styles/App-styles'; import { Spacer } from '../../common/Spacer'; -import Copy from '../../assets/svgs/Copy.svg'; -import { Loader } from '../Loader'; import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../common/useModal'; @@ -56,15 +45,18 @@ export const Minting = ({ const [isLoading, setIsLoading] = useState(false); const { show: showKey, message } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal(); + const theme = useTheme(); const [info, setInfo] = useState(null); const [names, setNames] = useState({}); const [accountInfos, setAccountInfos] = useState({}); const [showWaitDialog, setShowWaitDialog] = useState(false); + const isPartOfMintingGroup = useMemo(() => { if (groups?.length === 0) return false; return !!groups?.find((item) => item?.groupId?.toString() === '694'); }, [groups]); + const getMintingAccounts = useCallback(async () => { try { const url = `${getBaseApiReact()}/admin/mintingaccounts`; @@ -74,7 +66,9 @@ export const Minting = ({ } const data = await response.json(); setMintingAccounts(data); - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); const accountIsMinting = useMemo(() => { @@ -105,7 +99,7 @@ export const Minting = ({ }); } } catch (error) { - // error + console.log(error); } }; @@ -131,6 +125,7 @@ export const Minting = ({ setAccountInfo(data); } } catch (error) { + console.log(error); } finally { if (!others) { setIsLoading(false); @@ -199,7 +194,9 @@ export const Minting = ({ const data = await response.json(); setRewardShares(data); return data; - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); const addMintingAccount = useCallback(async (val) => { @@ -250,7 +247,6 @@ export const Minting = ({ window .sendMessage( 'ADMIN_ACTION', - { type: 'removemintingaccount', value: val, @@ -354,7 +350,6 @@ export const Minting = ({ if (findRewardShare) { return true; // Exit early if found } - await sleep(pollingInterval); // Wait before the next poll } @@ -507,7 +502,7 @@ export const Minting = ({ const _blocksNeed = () => { if (accountInfo?.level === 0) { - return 7200; + return 7200; // TODO manage these magic numbers in a proper location } else if (accountInfo?.level === 1) { return 72000; } else if (accountInfo?.level === 2) { @@ -558,11 +553,11 @@ export const Minting = ({ fullScreen sx={{ '& .MuiDialog-paper': { + height: '100vh', margin: 0, maxWidth: '100%', - width: '100%', - height: '100vh', overflow: 'hidden', // Prevent scrollbars + width: '100%', }, }} > @@ -579,6 +574,7 @@ export const Minting = ({ > + )} Account: {handleNames(accountInfo?.address)} + Level: {accountInfo?.level} + blocks remaining until next level: {_levelUpBlocks()} + This node is minting: {nodeInfos?.isMintingPossible?.toString()} @@ -626,11 +625,11 @@ export const Minting = ({ {isPartOfMintingGroup && !accountIsMinting && ( + + ))} @@ -740,7 +741,7 @@ export const Minting = ({ {!isPartOfMintingGroup && ( @@ -764,7 +765,7 @@ export const Minting = ({ size="small" sx={{ backgroundColor: 'var(--green)', - color: 'black', + color: theme.palette.text.primary, fontWeight: 'bold', opacity: 0.7, @@ -798,6 +799,7 @@ export const Minting = ({ {isShowNext ? 'Confirmed' : 'Please Wait'} + {!isShowNext && ( diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index 0485240..c760751 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -1,12 +1,15 @@ import { useMemo } from 'react'; -import QMailLogo from '../assets/QMailLogo.png'; +import EmailIcon from '@mui/icons-material/Email'; import { useRecoilState } from 'recoil'; import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global'; import { isLessThanOneWeekOld } from './Group/QMailMessages'; -import { ButtonBase, Tooltip } from '@mui/material'; +import { ButtonBase, Tooltip, useTheme } from '@mui/material'; import { executeEvent } from '../utils/events'; +import { Mail } from '@mui/icons-material'; export const QMailStatus = () => { + const theme = useTheme(); + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( qMailLastEnteredTimestampAtom ); @@ -24,6 +27,7 @@ export const QMailStatus = () => { return true; return false; }, [lastEnteredTimestamp, mails]); + return ( { @@ -38,21 +42,27 @@ export const QMailStatus = () => { {hasNewMail && (
)} + Q-MAIL } @@ -62,18 +72,18 @@ export const QMailStatus = () => { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} > - + ); diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index ec68533..d9baa65 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -1,5 +1,5 @@ -import { Box, CircularProgress } from '@mui/material'; -import React, { useEffect, useState } from 'react'; +import { Box, CircularProgress, useTheme } from '@mui/material'; +import { useState } from 'react'; import { CustomButton, CustomInput, @@ -13,6 +13,7 @@ import { ErrorText } from './ErrorText/ErrorText'; import { getFee } from '../background'; export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { + const theme = useTheme(); const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); const [paymentAmount, setPaymentAmount] = useState(0); const [paymentPassword, setPaymentPassword] = useState(''); @@ -42,7 +43,9 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { message: `Would you like to transfer ${Number(paymentAmount)} QORT?`, paymentFee: fee.fee + ' QORT', }); + setIsLoadingSendCoin(true); + window .sendMessage('sendCoin', { amount: Number(paymentAmount), @@ -62,65 +65,76 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { setIsLoadingSendCoin(false); }); } catch (error) { - // error + console.log(error); } }; + return ( <> Transfer QORT + + Balance: + {balance?.toFixed(2)} QORT + To + + setPaymentTo(e.target.value)} autoComplete="off" /> + + Amount + + { allowNegatives={false} afterChange={(e: string) => setPaymentAmount(+e)} /> + + Confirm Wallet Password + + { autoComplete="off" /> + + {sendPaymentError} {/* {sendPaymentSuccess} */} + + { )} diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 273da15..cf8dba6 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useEffect, useMemo, useState } from "react"; -import { useRecoilState, useSetRecoilState } from "recoil"; -import isEqual from "lodash/isEqual"; // Import deep comparison utility +import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { useRecoilState, useSetRecoilState } from 'recoil'; +import isEqual from 'lodash/isEqual'; // Import deep comparison utility import { canSaveSettingToQdnAtom, hasSettingsChangedAtom, @@ -9,35 +9,35 @@ import { settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, -} from "../../atoms/global"; -import { Box, Button, ButtonBase, Popover, Typography } from "@mui/material"; -import { objectToBase64 } from "../../qdn/encryption/group-encryption"; -import { MyContext } from "../../App"; -import { getFee } from "../../background"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { SaveIcon } from "../../assets/svgs/SaveIcon"; -import { IconWrapper } from "../Desktop/DesktopFooter"; -import { Spacer } from "../../common/Spacer"; -import { LoadingButton } from "@mui/lab"; -import { saveToLocalStorage } from "../Apps/AppsNavBar"; -import { decryptData, encryptData } from "../../qortalRequests/get"; -import { saveFileToDiskGeneric } from "../../utils/generateWallet/generateWallet"; +} from '../../atoms/global'; +import { Box, Button, ButtonBase, Popover, Typography } from '@mui/material'; +import { objectToBase64 } from '../../qdn/encryption/group-encryption'; +import { MyContext } from '../../App'; +import { getFee } from '../../background'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { SaveIcon } from '../../assets/Icons/SaveIcon'; +import { IconWrapper } from '../Desktop/DesktopFooter'; +import { Spacer } from '../../common/Spacer'; +import { LoadingButton } from '@mui/lab'; +import { saveToLocalStorage } from '../Apps/AppsNavBar'; +import { decryptData, encryptData } from '../../qortalRequests/get'; +import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet'; import { base64ToUint8Array, uint8ArrayToObject, -} from "../../backgroundFunctions/encryption"; +} from '../../backgroundFunctions/encryption'; export const handleImportClick = async () => { - const fileInput = document.createElement("input"); - fileInput.type = "file"; - fileInput.accept = ".base64,.txt"; + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.base64,.txt'; // Create a promise to handle file selection and reading synchronously return await new Promise((resolve, reject) => { fileInput.onchange = () => { const file = fileInput.files[0]; if (!file) { - reject(new Error("No file selected")); + reject(new Error('No file selected')); return; } @@ -46,7 +46,7 @@ export const handleImportClick = async () => { resolve(e.target.result); // Resolve with the file content }; reader.onerror = () => { - reject(new Error("Error reading file")); + reject(new Error('Error reading file')); }; reader.readAsText(file); // Read the file as text (Base64 string) @@ -124,7 +124,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const encryptData = await new Promise((res, rej) => { window .sendMessage( - "ENCRYPT_DATA", + 'ENCRYPT_DATA', { data64, }, @@ -139,23 +139,23 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }) .catch((error) => { - console.error("Failed qortalRequest", error); + console.error('Failed qortalRequest', error); }); }); if (encryptData && !encryptData?.error) { - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); await show({ message: - "Would you like to publish your settings to QDN (encrypted) ?", - publishFee: fee.fee + " QORT", + 'Would you like to publish your settings to QDN (encrypted) ?', + publishFee: fee.fee + ' QORT', }); const response = await new Promise((res, rej) => { window - .sendMessage("publishOnQDN", { + .sendMessage('publishOnQDN', { data: encryptData, - identifier: "ext_saved_settings", - service: "DOCUMENT_PRIVATE", + identifier: 'ext_saved_settings', + service: 'DOCUMENT_PRIVATE', }) .then((response) => { if (!response?.error) { @@ -165,15 +165,15 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); }); if (response?.identifier) { setOldPinnedApps(pinnedApps); setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ - type: "success", - message: "Sucessfully published to QDN", + type: 'success', + message: 'Sucessfully published to QDN', }); setOpenSnack(true); setAnchorEl(null); @@ -181,8 +181,8 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } } catch (error) { setInfoSnack({ - type: "error", - message: error?.message || "Unable to save to QDN", + type: 'error', + message: error?.message || 'Unable to save to QDN', }); setOpenSnack(true); } finally { @@ -196,7 +196,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const revertChanges = () => { setPinnedApps(oldPinnedApps); - saveToLocalStorage("ext_saved_settings", "sortablePinnedApps", null); + saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', null); setAnchorEl(null); }; @@ -218,11 +218,11 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { selected={false} > ) : ( - + )} { anchorEl={anchorEl} onClose={() => setAnchorEl(null)} // Close popover on click outside anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} sx={{ - width: "300px", - maxWidth: "90%", - maxHeight: "80%", - overflow: "auto", + width: '300px', + maxWidth: '90%', + maxHeight: '80%', + overflow: 'auto', }} > {isUsingImportExportSettings && ( You are using the export/import way of saving settings. @@ -274,8 +274,8 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { size="small" onClick={() => { saveToLocalStorage( - "ext_saved_settings_import_export", - "sortablePinnedApps", + 'ext_saved_settings_import_export', + 'sortablePinnedApps', null, true ); @@ -283,13 +283,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }} variant="contained" sx={{ - backgroundColor: "var(--danger)", - color: "black", - fontWeight: "bold", + backgroundColor: 'var(--danger)', + color: 'black', + fontWeight: 'bold', opacity: 0.7, - "&:hover": { - backgroundColor: "var(--danger)", - color: "black", + '&:hover': { + backgroundColor: 'var(--danger)', + color: 'black', opacity: 1, }, }} @@ -302,25 +302,25 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {!isUsingImportExportSettings && ( {!myName ? ( You need a registered Qortal name to save your pinned apps to @@ -332,15 +332,15 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {hasChanged && ( You have unsaved changes to your pinned apps. Save them to @@ -349,13 +349,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { { <> Don't like your current local changes? Would you @@ -385,13 +385,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { onClick={revertChanges} variant="contained" sx={{ - backgroundColor: "var(--danger)", - color: "black", - fontWeight: "bold", + backgroundColor: 'var(--danger)', + color: 'black', + fontWeight: 'bold', opacity: 0.7, - "&:hover": { - backgroundColor: "var(--danger)", - color: "black", + '&:hover': { + backgroundColor: 'var(--danger)', + color: 'black', opacity: 1, }, }} @@ -405,7 +405,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { <> Don't like your current local changes? Would you @@ -428,15 +428,15 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { isUsingImportExportSettings !== true && ( The app was unable to download your existing QDN-saved @@ -449,13 +449,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { onClick={saveToQdn} variant="contained" sx={{ - backgroundColor: "var(--danger)", - color: "black", - fontWeight: "bold", + backgroundColor: 'var(--danger)', + color: 'black', + fontWeight: 'bold', opacity: 0.7, - "&:hover": { - backgroundColor: "var(--danger)", - color: "black", + '&:hover': { + backgroundColor: 'var(--danger)', + color: 'black', opacity: 1, }, }} @@ -467,15 +467,15 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {!hasChanged && ( You currently do not have any changes to your pinned apps @@ -488,19 +488,19 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { )} { ); if (Array.isArray(responseData)) { saveToLocalStorage( - "ext_saved_settings_import_export", - "sortablePinnedApps", + 'ext_saved_settings_import_export', + 'sortablePinnedApps', responseData, { isUsingImportExport: true, @@ -529,7 +529,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setIsUsingImportExportSettings(true); } } catch (error) { - console.log("error", error); + console.log('error', error); } }} > @@ -544,14 +544,14 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { data64, }); const blob = new Blob([encryptedData], { - type: "text/plain", + type: 'text/plain', }); - const timestamp = new Date().toISOString().replace(/:/g, "-"); // Safe timestamp for filenames + const timestamp = new Date().toISOString().replace(/:/g, '-'); // Safe timestamp for filenames const filename = `qortal-new-ui-backup-settings-${timestamp}.txt`; await saveFileToDiskGeneric(blob, filename); } catch (error) { - console.log("error", error); + console.log('error', error); } }} > diff --git a/src/components/Snackbar/Snackbar.tsx b/src/components/Snackbar/Snackbar.tsx index 59fa295..d79677e 100644 --- a/src/components/Snackbar/Snackbar.tsx +++ b/src/components/Snackbar/Snackbar.tsx @@ -1,31 +1,36 @@ import * as React from 'react'; -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, duration}) => { - - - +export const CustomizedSnackbars = ({ + open, + setOpen, + info, + setInfo, + duration, +}) => { const handleClose = ( event?: React.SyntheticEvent | Event, - reason?: SnackbarCloseReason, + reason?: SnackbarCloseReason ) => { if (reason === 'clickaway') { return; } - setOpen(false); - setInfo(null) + setInfo(null); }; - if(!open) return null + if (!open) return null; + return (
- +
); -} \ No newline at end of file +}; diff --git a/src/components/TaskManager/TaskManger.tsx b/src/components/TaskManager/TaskManager.tsx similarity index 59% rename from src/components/TaskManager/TaskManger.tsx rename to src/components/TaskManager/TaskManager.tsx index a835ae1..f1f075b 100644 --- a/src/components/TaskManager/TaskManger.tsx +++ b/src/components/TaskManager/TaskManager.tsx @@ -5,19 +5,21 @@ import { ListItemText, Collapse, IconButton, -} from "@mui/material"; -import React, { useContext, useEffect, useRef } from "react"; -import PendingIcon from "@mui/icons-material/Pending"; -import TaskAltIcon from "@mui/icons-material/TaskAlt"; -import ExpandLess from "@mui/icons-material/ExpandLess"; -import ExpandMore from "@mui/icons-material/ExpandMore"; -import { MyContext, getBaseApiReact, isMobile } from "../../App"; -import { executeEvent } from "../../utils/events"; + useTheme, +} from '@mui/material'; +import React, { useContext, useEffect, useRef } from 'react'; +import PendingIcon from '@mui/icons-material/Pending'; +import TaskAltIcon from '@mui/icons-material/TaskAlt'; +import ExpandLess from '@mui/icons-material/ExpandLess'; +import ExpandMore from '@mui/icons-material/ExpandMore'; +import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import { executeEvent } from '../../utils/events'; export const TaskManager = ({ getUserInfo }) => { const { txList, setTxList, memberGroups } = useContext(MyContext); const [open, setOpen] = React.useState(false); const intervals = useRef({}); + const theme = useTheme(); const handleClick = () => { setOpen((prev) => !prev); @@ -58,7 +60,9 @@ export const TaskManager = ({ getUserInfo }) => { } clearInterval(intervals.current[signature]); } - } catch (error) {} + } catch (error) { + console.log(error); + } stop = false; } }; @@ -71,7 +75,7 @@ export const TaskManager = ({ getUserInfo }) => { let previousData = [...prev]; memberGroups.forEach((group) => { const findGroup = txList.findIndex( - (tx) => tx?.type === "joined-group" && tx?.groupId === group.groupId + (tx) => tx?.type === 'joined-group' && tx?.groupId === group.groupId ); if (findGroup !== -1 && !previousData[findGroup]?.done) { previousData[findGroup].done = true; @@ -81,7 +85,7 @@ export const TaskManager = ({ getUserInfo }) => { memberGroups.forEach((group) => { const findGroup = txList.findIndex( (tx) => - tx?.type === "created-group" && tx?.groupName === group.groupName + tx?.type === 'created-group' && tx?.groupName === group.groupName ); if (findGroup !== -1 && !previousData[findGroup]?.done) { previousData[findGroup].done = true; @@ -90,49 +94,52 @@ export const TaskManager = ({ getUserInfo }) => { prev.forEach((tx, index) => { if ( - tx?.type === "leave-group" && - memberGroups.findIndex((group) => tx?.groupId === group.groupId) === -1 + tx?.type === 'leave-group' && + memberGroups.findIndex((group) => tx?.groupId === group.groupId) === + -1 ) { previousData[index].done = true; } }); - - return previousData; }); }, [memberGroups, getUserInfo]); - useEffect(()=> { - - txList.forEach((tx) => { - if ( - ["created-common-secret", "joined-group-request", "join-request-accept"].includes( - tx?.type - ) && - tx?.signature && - !tx.done - ) { - if (!intervals.current[tx.signature]) { - getStatus({ signature: tx.signature }); - } + useEffect(() => { + txList.forEach((tx) => { + if ( + [ + 'created-common-secret', + 'joined-group-request', + 'join-request-accept', + ].includes(tx?.type) && + tx?.signature && + !tx.done + ) { + if (!intervals.current[tx.signature]) { + getStatus({ signature: tx.signature }); } - if (tx?.type === "register-name" && tx?.signature && !tx.done) { - if (!intervals.current[tx.signature]) { - getStatus({ signature: tx.signature }, getUserInfo); - } + } + if (tx?.type === 'register-name' && tx?.signature && !tx.done) { + if (!intervals.current[tx.signature]) { + getStatus({ signature: tx.signature }, getUserInfo); } - if((tx?.type === "remove-rewardShare" || tx?.type === "add-rewardShare") && tx?.signature && !tx.done){ - if (!intervals.current[tx.signature]) { - const sendEventForRewardShare = ()=> { - executeEvent('refresh-rewardshare-list', {}) - } - getStatus({ signature: tx.signature }, sendEventForRewardShare); - } + } + if ( + (tx?.type === 'remove-rewardShare' || tx?.type === 'add-rewardShare') && + tx?.signature && + !tx.done + ) { + if (!intervals.current[tx.signature]) { + const sendEventForRewardShare = () => { + executeEvent('refresh-rewardshare-list', {}); + }; + getStatus({ signature: tx.signature }, sendEventForRewardShare); } - }); - - }, [txList]) + } + }); + }, [txList]); if (isMobile || txList?.length === 0 || txList.every((item) => item?.done)) return null; @@ -143,43 +150,48 @@ export const TaskManager = ({ getUserInfo }) => { - {txList.some((item) => !item.done) ? : } + {txList.some((item) => !item.done) ? ( + + ) : ( + + )} )} {open && ( - + {txList.some((item) => !item.done) ? ( - + ) : ( - + )} diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index e64c183..5a5abb8 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -1,6 +1,7 @@ import { createContext, useContext, useState, useMemo } from 'react'; import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; -import { darkTheme, lightTheme } from '../../styles/theme'; +import { darkTheme } from '../../styles/theme-dark'; +import { lightTheme } from '../../styles/theme-light'; const ThemeContext = createContext({ themeMode: 'light', diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index 300ca2f..0b19ba1 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { DrawerUserLookup } from "../Drawer/DrawerUserLookup"; +import { useCallback, useEffect, useState } from 'react'; +import { DrawerUserLookup } from '../Drawer/DrawerUserLookup'; import { Avatar, Box, @@ -16,16 +16,21 @@ import { Typography, Table, CircularProgress, -} from "@mui/material"; -import { getAddressInfo, getNameOrAddress } from "../../background"; -import { getBaseApiReact } from "../../App"; -import { getNameInfo } from "../Group/Group"; -import AccountCircleIcon from "@mui/icons-material/AccountCircle"; -import { Spacer } from "../../common/Spacer"; -import { formatTimestamp } from "../../utils/time"; + useTheme, +} from '@mui/material'; +import { getAddressInfo, getNameOrAddress } from '../../background'; +import { getBaseApiReact } from '../../App'; +import { getNameInfo } from '../Group/Group'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; +import { Spacer } from '../../common/Spacer'; +import { formatTimestamp } from '../../utils/time'; import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; import SearchIcon from '@mui/icons-material/Search'; -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from '../../utils/events'; function formatAddress(str) { if (str.length <= 12) return str; @@ -37,469 +42,512 @@ function formatAddress(str) { } export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { - const [nameOrAddress, setNameOrAddress] = useState(""); - const [errorMessage, setErrorMessage] = useState(""); + const theme = useTheme(); + const [nameOrAddress, setNameOrAddress] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); const [addressInfo, setAddressInfo] = useState(null); const [isLoadingUser, setIsLoadingUser] = useState(false); const [isLoadingPayments, setIsLoadingPayments] = useState(false); const [payments, setPayments] = useState([]); - const lookupFunc = useCallback(async (messageAddressOrName) => { - try { - setErrorMessage('') - setIsLoadingUser(true) - setPayments([]) - setAddressInfo(null) - const inputAddressOrName = messageAddressOrName || nameOrAddress - if (!inputAddressOrName?.trim()) - throw new Error("Please insert a name or address"); - const owner = await getNameOrAddress(inputAddressOrName); - if (!owner) throw new Error("Name does not exist"); - const addressInfoRes = await getAddressInfo(owner); - if (!addressInfoRes?.publicKey) { - throw new Error("Address does not exist on blockchain"); + const lookupFunc = useCallback( + async (messageAddressOrName) => { + try { + setErrorMessage(''); + setIsLoadingUser(true); + setPayments([]); + setAddressInfo(null); + const inputAddressOrName = messageAddressOrName || nameOrAddress; + + if (!inputAddressOrName?.trim()) + throw new Error('Please insert a name or address'); + const owner = await getNameOrAddress(inputAddressOrName); + if (!owner) throw new Error('Name does not exist'); + + const addressInfoRes = await getAddressInfo(owner); + if (!addressInfoRes?.publicKey) { + throw new Error('Address does not exist on blockchain'); + } + + const name = await getNameInfo(owner); + const balanceRes = await fetch( + `${getBaseApiReact()}/addresses/balance/${owner}` + ); + + const balanceData = await balanceRes.json(); + setAddressInfo({ + ...addressInfoRes, + balance: balanceData, + name, + }); + setIsLoadingUser(false); + setIsLoadingPayments(true); + + const getPayments = await fetch( + `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true` + ); + const paymentsData = await getPayments.json(); + setPayments(paymentsData); + } catch (error) { + setErrorMessage(error?.message); + console.error(error); + } finally { + setIsLoadingUser(false); + setIsLoadingPayments(false); } - const name = await getNameInfo(owner); - const balanceRes = await fetch( - `${getBaseApiReact()}/addresses/balance/${owner}` - ); - const balanceData = await balanceRes.json(); - setAddressInfo({ - ...addressInfoRes, - balance: balanceData, - name, - }); - setIsLoadingUser(false) - setIsLoadingPayments(true) + }, + [nameOrAddress] + ); - const getPayments = await fetch( - `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true` - ); - const paymentsData = await getPayments.json(); - setPayments(paymentsData); - - } catch (error) { - setErrorMessage(error?.message) - console.error(error); - } finally { - setIsLoadingUser(false) - setIsLoadingPayments(false) - } - }, [nameOrAddress]); - - const openUserLookupDrawerFunc = useCallback((e) => { - setIsOpenDrawerLookup(true) + const openUserLookupDrawerFunc = useCallback( + (e) => { + setIsOpenDrawerLookup(true); const message = e.detail?.addressOrName; - if(message){ - lookupFunc(message) + if (message) { + lookupFunc(message); } - }, [lookupFunc, setIsOpenDrawerLookup]); - - useEffect(() => { - subscribeToEvent("openUserLookupDrawer", openUserLookupDrawerFunc); - - return () => { - unsubscribeFromEvent("openUserLookupDrawer", openUserLookupDrawerFunc); - }; - }, [openUserLookupDrawerFunc]); + }, + [lookupFunc, setIsOpenDrawerLookup] + ); - const onClose = ()=> { - setIsOpenDrawerLookup(false) - setNameOrAddress('') - setErrorMessage('') - setPayments([]) - setIsLoadingUser(false) - setIsLoadingPayments(false) - setAddressInfo(null) - } + useEffect(() => { + subscribeToEvent('openUserLookupDrawer', openUserLookupDrawerFunc); + return () => { + unsubscribeFromEvent('openUserLookupDrawer', openUserLookupDrawerFunc); + }; + }, [openUserLookupDrawerFunc]); + + const onClose = () => { + setIsOpenDrawerLookup(false); + setNameOrAddress(''); + setErrorMessage(''); + setPayments([]); + setIsLoadingUser(false); + setIsLoadingPayments(false); + setAddressInfo(null); + }; return ( - - - setNameOrAddress(e.target.value)} size="small" placeholder="Address or Name" autoComplete="off" onKeyDown={(e) => { - if (e.key === "Enter" && nameOrAddress) { + if (e.key === 'Enter' && nameOrAddress) { lookupFunc(); } }} /> - { - lookupFunc(); - }} > - + { + lookupFunc(); + }} + > + - { - onClose() - }}> - + { + onClose(); + }} + > + - {!isLoadingUser && errorMessage && ( - - {errorMessage} + marginTop: '40px', + width: '100%', + }} + > + {errorMessage} - )} - {isLoadingUser && ( - - + marginTop: '40px', + width: '100%', + }} + > + - )} - {!isLoadingUser && addressInfo && ( + )} + {!isLoadingUser && addressInfo && ( <> - - - - - - {addressInfo?.name ?? "Name not registered"} - - - - {addressInfo?.name ? ( - - - - ) : ( - - )} - - - - Level {addressInfo?.level} - - - + + + + {addressInfo?.name ?? 'Name not registered'} + + + + + + {addressInfo?.name ? ( + + + + ) : ( + + )} + + + + + + Level {addressInfo?.level} + + + + + + + Address + + + copy address + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, + }} + > + { + navigator.clipboard.writeText(addressInfo?.address); + }} + > + + {addressInfo?.address} + + + + + + + Balance + {addressInfo?.balance} + + + + + + + + + )} + + + + {isLoadingPayments && ( + + + + )} + {!isLoadingPayments && addressInfo && ( + + 20 most recent payments + + + + {!isLoadingPayments && payments?.length === 0 && ( - Address + No payments - - copy address - - } - placement="bottom" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - navigator.clipboard.writeText(addressInfo?.address); - }} - > - - {addressInfo?.address} - - - - - - Balance - {addressInfo?.balance} - - - - - - + )} - - )} - - {isLoadingPayments && ( - - - - )} - {!isLoadingPayments && addressInfo && ( - - 20 most recent payments - - {!isLoadingPayments && payments?.length === 0 && ( - - No payments - - )} - - - - Sender - Reciver - Amount - Time - - - - {payments.map((payment, index) => ( - - - - copy address - - } - placement="bottom" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - navigator.clipboard.writeText( - payment?.creatorAddress - ); - }} - > - {formatAddress(payment?.creatorAddress)} - - - - - - copy address - - } - placement="bottom" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - navigator.clipboard.writeText(payment?.recipient); - }} - > - {formatAddress(payment?.recipient)} - - - - - - {payment?.amount} - - {formatTimestamp(payment?.timestamp)} - - ))} - -
-
+ + + + Sender + Reciver + Amount + Time + + + + + {payments.map((payment, index) => ( + + + + copy address + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: + theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.creatorAddress + ); + }} + > + {formatAddress(payment?.creatorAddress)} + + + + + + + copy address + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: + theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, + }} + > + { + navigator.clipboard.writeText(payment?.recipient); + }} + > + {formatAddress(payment?.recipient)} + + + + {payment?.amount} + + {formatTimestamp(payment?.timestamp)} + + + ))} + +
+ )} -
diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index c074c16..44d7c12 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -1,10 +1,17 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { Popover, Button, Box, CircularProgress } from '@mui/material'; +import { useCallback, useContext, useEffect, useState } from 'react'; +import { + Popover, + Button, + Box, + CircularProgress, + useTheme, +} from '@mui/material'; import { executeEvent } from '../utils/events'; import { MyContext } from '../App'; export const WrapperUserAction = ({ children, address, name, disabled }) => { - const {isRunningPublicNode} = useContext(MyContext) + const theme = useTheme(); + const { isRunningPublicNode } = useContext(MyContext); const [anchorEl, setAnchorEl] = useState(null); // Handle child element click to open Popover @@ -22,8 +29,8 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { const open = Boolean(anchorEl); const id = open ? address || name : undefined; - if(disabled){ - return children + if (disabled) { + return children; } return ( @@ -31,16 +38,16 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { {/* Render the child without altering dimensions */} @@ -49,159 +56,151 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { {/* Popover */} {open && ( - event.stopPropagation(), // Stop propagation inside popover - }, - }} - > - - {/* Option 1: Message */} - - - {/* Option 2: Send QORT */} - - - + + {/* Option 2: Send QORT */} + + + + - {!isRunningPublicNode && ( - - - )} - - + {!isRunningPublicNode && ( + + )} + +
)} ); }; +const BlockUser = ({ address, name, handleClose }) => { + const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const { isUserBlocked, addToBlockList, removeBlockFromList } = + useContext(MyContext); + const theme = useTheme(); -const BlockUser = ({address, name, handleClose})=> { - const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null) - const [isLoading, setIsLoading] = useState(false) - const {isUserBlocked, - addToBlockList, - removeBlockFromList} = useContext(MyContext) - -useEffect(()=> { - if(!address) return - setIsAlreadyBlocked(isUserBlocked(address, name)) -}, [address, setIsAlreadyBlocked, isUserBlocked, name]) + useEffect(() => { + if (!address) return; + setIsAlreadyBlocked(isUserBlocked(address, name)); + }, [address, setIsAlreadyBlocked, isUserBlocked, name]); return ( - ) -} \ No newline at end of file + }} + > + {(isAlreadyBlocked === null || isLoading) && ( + + )} + {isAlreadyBlocked && 'Unblock name'} + {isAlreadyBlocked === false && 'Block name'} + + ); +}; diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx index b5df816..cc6e4e5 100644 --- a/src/hooks/useHandlePaymentNotification.tsx +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { getBaseApiReact } from '../App'; import { getData, storeData } from '../utils/chromeStorage'; import { checkDifference, getNameInfoForOthers } from '../background'; @@ -7,126 +7,131 @@ import { lastPaymentSeenTimestampAtom } from '../atoms/global'; import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; export const useHandlePaymentNotification = (address) => { - const [latestTx, setLatestTx] = useState(null); + const [latestTx, setLatestTx] = useState(null); - const nameAddressOfSender = useRef({}) - const isFetchingName = useRef({}) - - - const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = - useRecoilState(lastPaymentSeenTimestampAtom); - - useEffect(() => { - if (lastEnteredTimestampPayment && address) { - storeData(`last-seen-payment-${address}`, Date.now()).catch((error) => { - console.error(error); - }); - } - }, [lastEnteredTimestampPayment, address]); - - const getNameOrAddressOfSender = useCallback(async(senderAddress)=> { - if(isFetchingName.current[senderAddress]) return senderAddress - try { - isFetchingName.current[senderAddress] = true - const res = await getNameInfoForOthers(senderAddress) - nameAddressOfSender.current[senderAddress] = res || senderAddress - } catch (error) { - console.error(error) - } finally { - isFetchingName.current[senderAddress] = false - } - - }, []) + const nameAddressOfSender = useRef({}); + const isFetchingName = useRef({}); - const getNameOrAddressOfSenderMiddle = useCallback(async(senderAddress)=> { - getNameOrAddressOfSender(senderAddress) - return senderAddress - - }, [getNameOrAddressOfSender]) - - const hasNewPayment = useMemo(() => { - if (!latestTx) return false; - if (!checkDifference(latestTx?.timestamp)) return false; - if ( - !lastEnteredTimestampPayment || - lastEnteredTimestampPayment < latestTx?.timestamp - ) - return true; - - return false; - }, [lastEnteredTimestampPayment, latestTx]); - - const getLastSeenData = useCallback(async () => { - try { - if (!address) return; - const key = `last-seen-payment-${address}`; - - const res = await getData(key).catch(() => null); - if (res) { - setLastEnteredTimestampPayment(res); - } - - const response = await fetch( - `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` - ); - - const responseData = await response.json(); - - const latestTx = responseData.filter( - (tx) => tx?.creatorAddress !== address && tx?.recipient === address - )[0]; - if (!latestTx) { - return; // continue to the next group - } - - setLatestTx(latestTx); - } catch (error) { + const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = + useRecoilState(lastPaymentSeenTimestampAtom); + + useEffect(() => { + if (lastEnteredTimestampPayment && address) { + storeData(`last-seen-payment-${address}`, Date.now()).catch((error) => { console.error(error); - } - }, [address, setLastEnteredTimestampPayment]); - - - useEffect(() => { - getLastSeenData(); - // Handler function for incoming messages - const messageHandler = (event) => { - if (event.origin !== window.location.origin) { - return; - } - const message = event.data; - if (message?.action === "SET_PAYMENT_ANNOUNCEMENT" && message?.payload) { - setLatestTx(message.payload); - } - }; - - // Attach the event listener - window.addEventListener("message", messageHandler); - - // Clean up the event listener on component unmount - return () => { - window.removeEventListener("message", messageHandler); - }; - }, [getLastSeenData]); + }); + } + }, [lastEnteredTimestampPayment, address]); - const setLastEnteredTimestampPaymentEventFunc = useCallback( - (e) => { - setLastEnteredTimestampPayment(Date.now) - }, - [setLastEnteredTimestampPayment] + const getNameOrAddressOfSender = useCallback(async (senderAddress) => { + if (isFetchingName.current[senderAddress]) return senderAddress; + try { + isFetchingName.current[senderAddress] = true; + const res = await getNameInfoForOthers(senderAddress); + nameAddressOfSender.current[senderAddress] = res || senderAddress; + } catch (error) { + console.error(error); + } finally { + isFetchingName.current[senderAddress] = false; + } + }, []); + + const getNameOrAddressOfSenderMiddle = useCallback( + async (senderAddress) => { + getNameOrAddressOfSender(senderAddress); + return senderAddress; + }, + [getNameOrAddressOfSender] + ); + + const hasNewPayment = useMemo(() => { + if (!latestTx) return false; + if (!checkDifference(latestTx?.timestamp)) return false; + if ( + !lastEnteredTimestampPayment || + lastEnteredTimestampPayment < latestTx?.timestamp + ) + return true; + + return false; + }, [lastEnteredTimestampPayment, latestTx]); + + const getLastSeenData = useCallback(async () => { + try { + if (!address) return; + const key = `last-seen-payment-${address}`; + + const res = await getData(key).catch(() => null); + if (res) { + setLastEnteredTimestampPayment(res); + } + + const response = await fetch( + `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` ); - - useEffect(() => { - subscribeToEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc); - - return () => { - unsubscribeFromEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc); - }; - }, [setLastEnteredTimestampPaymentEventFunc]); + + const responseData = await response.json(); + + const latestTx = responseData.filter( + (tx) => tx?.creatorAddress !== address && tx?.recipient === address + )[0]; + if (!latestTx) { + return; // continue to the next group + } + + setLatestTx(latestTx); + } catch (error) { + console.error(error); + } + }, [address, setLastEnteredTimestampPayment]); + + useEffect(() => { + getLastSeenData(); + // Handler function for incoming messages + const messageHandler = (event) => { + if (event.origin !== window.location.origin) { + return; + } + const message = event.data; + if (message?.action === 'SET_PAYMENT_ANNOUNCEMENT' && message?.payload) { + setLatestTx(message.payload); + } + }; + + // Attach the event listener + window.addEventListener('message', messageHandler); + + // Clean up the event listener on component unmount + return () => { + window.removeEventListener('message', messageHandler); + }; + }, [getLastSeenData]); + + const setLastEnteredTimestampPaymentEventFunc = useCallback( + (e) => { + setLastEnteredTimestampPayment(Date.now); + }, + [setLastEnteredTimestampPayment] + ); + + useEffect(() => { + subscribeToEvent( + 'setLastEnteredTimestampPaymentEvent', + setLastEnteredTimestampPaymentEventFunc + ); + + return () => { + unsubscribeFromEvent( + 'setLastEnteredTimestampPaymentEvent', + setLastEnteredTimestampPaymentEventFunc + ); + }; + }, [setLastEnteredTimestampPaymentEventFunc]); return { latestTx, getNameOrAddressOfSenderMiddle, hasNewPayment, setLastEnteredTimestampPayment, - nameAddressOfSender - } -} + nameAddressOfSender, + }; +}; diff --git a/src/main.tsx b/src/main.tsx index 3f04c89..69e8901 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,10 +5,12 @@ import './messaging/messagesToBackground'; import { MessageQueueProvider } from './MessageQueueContext.tsx'; import { RecoilRoot } from 'recoil'; import { ThemeProvider } from './components/Theme/ThemeContext.tsx'; +import { CssBaseline } from '@mui/material'; ReactDOM.createRoot(document.getElementById('root')!).render( <> + diff --git a/src/qortalRequests copy.ts b/src/qortalRequests copy.ts deleted file mode 100644 index 6807fe9..0000000 --- a/src/qortalRequests copy.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { addForeignServer, addListItems, createPoll, decryptData, deleteListItems, deployAt, encryptData, getCrossChainServerInfo, getDaySummary, getForeignFee, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getWalletBalance, joinGroup, publishMultipleQDNResources, publishQDNResource, removeForeignServer, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, updateForeignFee, voteOnPoll } from "./qortalRequests/get"; - - - -// Promisify chrome.storage.local.get -function getLocalStorage(key) { - return new Promise((resolve, reject) => { - chrome.storage.local.get([key], function (result) { - if (chrome.runtime.lastError) { - return reject(chrome.runtime.lastError); - } - resolve(result[key]); - }); - }); - } - - // Promisify chrome.storage.local.set - function setLocalStorage(data) { - return new Promise((resolve, reject) => { - chrome.storage.local.set(data, function () { - if (chrome.runtime.lastError) { - return reject(chrome.runtime.lastError); - } - resolve(); - }); - }); - } - - - export async function setPermission(key, value) { - try { - // Get the existing qortalRequestPermissions object - const qortalRequestPermissions = (await getLocalStorage('qortalRequestPermissions')) || {}; - - // Update the permission - qortalRequestPermissions[key] = value; - - // Save the updated object back to storage - await setLocalStorage({ qortalRequestPermissions }); - - } catch (error) { - console.error('Error setting permission:', error); - } - } - - export async function getPermission(key) { - try { - // Get the qortalRequestPermissions object from storage - const qortalRequestPermissions = (await getLocalStorage('qortalRequestPermissions')) || {}; - - // Return the value for the given key, or null if it doesn't exist - return qortalRequestPermissions[key] || null; - } catch (error) { - console.error('Error getting permission:', error); - return null; - } - } - - - // TODO: GET_FRIENDS_LIST - // NOT SURE IF TO IMPLEMENT: LINK_TO_QDN_RESOURCE, QDN_RESOURCE_DISPLAYED, SET_TAB_NOTIFICATIONS - -chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { - if (request) { - const isFromExtension = request?.isExtension - switch (request.action) { - case "GET_USER_ACCOUNT": { - getUserAccount() - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: "Unable to get user account" }); - }); - - break; - } - case "ENCRYPT_DATA": { - const data = request.payload; - - encryptData(data, sender) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "DECRYPT_DATA": { - const data = request.payload; - - decryptData(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "GET_LIST_ITEMS": { - const data = request.payload; - - getListItems(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "ADD_LIST_ITEMS": { - const data = request.payload; - - addListItems(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "DELETE_LIST_ITEM": { - const data = request.payload; - - deleteListItems(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "PUBLISH_QDN_RESOURCE": { - const data = request.payload; - - publishQDNResource(data, sender, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "PUBLISH_MULTIPLE_QDN_RESOURCES": { - const data = request.payload; - - publishMultipleQDNResources(data, sender, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "VOTE_ON_POLL": { - const data = request.payload; - - voteOnPoll(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "CREATE_POLL": { - const data = request.payload; - - createPoll(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "SEND_CHAT_MESSAGE": { - const data = request.payload; - sendChatMessage(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "JOIN_GROUP": { - const data = request.payload; - - joinGroup(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "SAVE_FILE": { - const data = request.payload; - - saveFile(data, sender, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "DEPLOY_AT": { - const data = request.payload; - - deployAt(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "GET_USER_WALLET": { - const data = request.payload; - - getUserWallet(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "GET_WALLET_BALANCE": { - const data = request.payload; - - getWalletBalance(data, false, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "GET_USER_WALLET_INFO": { - const data = request.payload; - - getUserWalletInfo(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "GET_CROSSCHAIN_SERVER_INFO": { - const data = request.payload; - - getCrossChainServerInfo(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - case "GET_TX_ACTIVITY_SUMMARY": { - const data = request.payload; - - getTxActivitySummary(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "GET_FOREIGN_FEE": { - const data = request.payload; - - getForeignFee(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "UPDATE_FOREIGN_FEE": { - const data = request.payload; - - updateForeignFee(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "GET_SERVER_CONNECTION_HISTORY": { - const data = request.payload; - - getServerConnectionHistory(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "SET_CURRENT_FOREIGN_SERVER": { - const data = request.payload; - - setCurrentForeignServer(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "ADD_FOREIGN_SERVER": { - const data = request.payload; - - addForeignServer(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "REMOVE_FOREIGN_SERVER": { - const data = request.payload; - - removeForeignServer(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "GET_DAY_SUMMARY": { - const data = request.payload; - - getDaySummary(data) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - - case "SEND_COIN": { - const data = request.payload; - - sendCoin(data, isFromExtension) - .then((res) => { - sendResponse(res); - }) - .catch((error) => { - sendResponse({ error: error.message }); - }); - - break; - } - } - } - return true; -}); diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index f034dbd..701c433 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,12 +1,73 @@ -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, updateGroupRequest, buyNameRequest, sellNameRequest, cancelSellNameRequest } from "./qortalRequests/get"; -import { getData, storeData } from "./utils/chromeStorage"; -import { executeEvent } from "./utils/events"; +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, + updateGroupRequest, + buyNameRequest, + sellNameRequest, + cancelSellNameRequest, +} from './qortalRequests/get'; +import { getData, storeData } from './utils/chromeStorage'; +import { executeEvent } from './utils/events'; function getLocalStorage(key) { return getData(key).catch((error) => { - console.error("Error retrieving data:", error); + console.error('Error retrieving data:', error); throw error; }); } @@ -14,1311 +75,1769 @@ function getLocalStorage(key) { // Promisify setting data in localStorage function setLocalStorage(key, data) { return storeData(key, data).catch((error) => { - console.error("Error saving data:", error); + console.error('Error saving data:', error); throw error; }); } -export const isRunningGateway = async ()=> { - let isGateway = true; - const apiKey = await getApiKeyFromStorage(); - if (apiKey && (apiKey?.url && !gateways.some(gateway => apiKey?.url?.includes(gateway)))) { - isGateway = false; - } +export const isRunningGateway = async () => { + let isGateway = true; + const apiKey = await getApiKeyFromStorage(); + if ( + apiKey && + apiKey?.url && + !gateways.some((gateway) => apiKey?.url?.includes(gateway)) + ) { + isGateway = false; + } - return isGateway + return isGateway; +}; + +export async function setPermission(key, value) { + try { + // Get the existing qortalRequestPermissions object + const qortalRequestPermissions = + (await getLocalStorage('qortalRequestPermissions')) || {}; + + // Update the permission + qortalRequestPermissions[key] = value; + + // Save the updated object back to storage + await setLocalStorage('qortalRequestPermissions', qortalRequestPermissions); + } catch (error) { + console.error('Error setting permission:', error); + } } - - export async function setPermission(key, value) { - try { - // Get the existing qortalRequestPermissions object - const qortalRequestPermissions = (await getLocalStorage('qortalRequestPermissions')) || {}; - - // Update the permission - qortalRequestPermissions[key] = value; - - // Save the updated object back to storage - await setLocalStorage('qortalRequestPermissions', qortalRequestPermissions ); - - } catch (error) { - console.error('Error setting permission:', error); - } +export async function getPermission(key) { + try { + // Get the qortalRequestPermissions object from storage + const qortalRequestPermissions = + (await getLocalStorage('qortalRequestPermissions')) || {}; + + // Return the value for the given key, or null if it doesn't exist + return qortalRequestPermissions[key] || null; + } catch (error) { + console.error('Error getting permission:', error); + return null; } +} - export async function getPermission(key) { - try { - // Get the qortalRequestPermissions object from storage - const qortalRequestPermissions = (await getLocalStorage('qortalRequestPermissions')) || {}; - - // Return the value for the given key, or null if it doesn't exist - return qortalRequestPermissions[key] || null; - } catch (error) { - console.error('Error getting permission:', error); - return null; - } - } +// TODO: GET_FRIENDS_LIST +// NOT SURE IF TO IMPLEMENT: LINK_TO_QDN_RESOURCE, QDN_RESOURCE_DISPLAYED, SET_TAB_NOTIFICATIONS +function setupMessageListenerQortalRequest() { + window.addEventListener('message', async (event) => { + const request = event.data; - // TODO: GET_FRIENDS_LIST - // NOT SURE IF TO IMPLEMENT: LINK_TO_QDN_RESOURCE, QDN_RESOURCE_DISPLAYED, SET_TAB_NOTIFICATIONS + // Ensure the message is from a trusted source + const isFromExtension = request?.isExtension; + const appInfo = request?.appInfo; + const skipAuth = request?.skipAuth || false; + if (request?.type !== 'backgroundMessage') return; // Only process messages of type 'backgroundMessage' - function setupMessageListenerQortalRequest() { - window.addEventListener("message", async (event) => { - const request = event.data; - - // Ensure the message is from a trusted source - const isFromExtension = request?.isExtension; - const appInfo = request?.appInfo; - const skipAuth = request?.skipAuth || false - if (request?.type !== "backgroundMessage") return; // Only process messages of type 'backgroundMessage' - - - // Handle actions based on the `request.action` value - switch (request.action) { - case "GET_USER_ACCOUNT": { - try { - const res = await getUserAccount({isFromExtension, appInfo, skipAuth}); - event.source.postMessage({ + // Handle actions based on the `request.action` value + switch (request.action) { + case 'GET_USER_ACCOUNT': { + try { + const res = await getUserAccount({ + isFromExtension, + appInfo, + skipAuth, + }); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, - error: "Unable to get user account", - type: "backgroundMessageResponse", - }, event.origin); - } - break; + error: 'Unable to get user account', + type: 'backgroundMessageResponse', + }, + event.origin + ); } - - case "ENCRYPT_DATA": { - try { - const res = await encryptData(request.payload, event.source); - event.source.postMessage({ + break; + } + + case 'ENCRYPT_DATA': { + try { + const res = await encryptData(request.payload, event.source); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "ENCRYPT_QORTAL_GROUP_DATA": { - try { - const res = await encryptQortalGroupData(request.payload, event.source); - event.source.postMessage({ + case 'ENCRYPT_QORTAL_GROUP_DATA': { + try { + const res = await encryptQortalGroupData( + request.payload, + event.source + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "DECRYPT_QORTAL_GROUP_DATA": { - try { - const res = await decryptQortalGroupData(request.payload, event.source); - event.source.postMessage({ + case 'DECRYPT_QORTAL_GROUP_DATA': { + try { + const res = await decryptQortalGroupData( + request.payload, + event.source + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "DECRYPT_DATA": { - try { - const res = await decryptData(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_LIST_ITEMS": { - try { - const res = await getListItems(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "ADD_LIST_ITEMS": { - try { - const res = await addListItems(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "DELETE_LIST_ITEM": { - try { - const res = await deleteListItems(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "PUBLISH_QDN_RESOURCE": { - try { - const res = await publishQDNResource(request.payload, event.source, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "PUBLISH_MULTIPLE_QDN_RESOURCES": { - try { - const res = await publishMultipleQDNResources(request.payload, event.source, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "VOTE_ON_POLL": { - try { - const res = await voteOnPoll(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "CREATE_POLL": { - try { - const res = await createPoll(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "SEND_CHAT_MESSAGE": { - try { - const res = await sendChatMessage(request.payload, isFromExtension, appInfo); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "JOIN_GROUP": { - try { - const res = await joinGroup(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "DEPLOY_AT": { - try { - const res = await deployAt(request.payload, isFromExtension); - event.source.postMessage({ + case 'DECRYPT_DATA': { + try { + const res = await decryptData(request.payload); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_USER_WALLET": { - try { - const res = await getUserWallet(request.payload, isFromExtension, appInfo); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_WALLET_BALANCE": { - try { - const res = await getWalletBalance(request.payload, false, isFromExtension, appInfo); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_USER_WALLET_TRANSACTIONS": { - try { - const res = await getUserWalletTransactions(request.payload, isFromExtension, appInfo); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_USER_WALLET_INFO": { - try { - const res = await getUserWalletInfo(request.payload, isFromExtension, appInfo); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_CROSSCHAIN_SERVER_INFO": { - try { - const res = await getCrossChainServerInfo(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_TX_ACTIVITY_SUMMARY": { - try { - const res = await getTxActivitySummary(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_FOREIGN_FEE": { - try { - const res = await getForeignFee(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "UPDATE_FOREIGN_FEE": { - try { - const res = await updateForeignFee(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_SERVER_CONNECTION_HISTORY": { - try { - const res = await getServerConnectionHistory(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "SET_CURRENT_FOREIGN_SERVER": { - try { - const res = await setCurrentForeignServer(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "ADD_FOREIGN_SERVER": { - try { - const res = await addForeignServer(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "REMOVE_FOREIGN_SERVER": { - try { - const res = await removeForeignServer(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_DAY_SUMMARY": { - try { - const res = await getDaySummary(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - - case "GET_NODE_INFO": { - try { - const res = await getNodeInfo(request.payload); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "GET_NODE_STATUS": { - try { - const res = await getNodeStatus(request.payload); - event.source.postMessage({ + case 'GET_LIST_ITEMS': { + try { + const res = await getListItems(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "SEND_COIN": { - try { - const res = await sendCoin(request.payload, isFromExtension); - event.source.postMessage({ + case 'ADD_LIST_ITEMS': { + try { + const res = await addListItems(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CREATE_TRADE_BUY_ORDER": { - try { - const res = await createBuyOrder(request.payload, isFromExtension); - event.source.postMessage({ + case 'DELETE_LIST_ITEM': { + try { + const res = await deleteListItems(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CREATE_TRADE_SELL_ORDER": { - try { - const res = await createSellOrder(request.payload, isFromExtension); - event.source.postMessage({ + case 'PUBLISH_QDN_RESOURCE': { + try { + const res = await publishQDNResource( + request.payload, + event.source, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CANCEL_TRADE_SELL_ORDER": { - try { - const res = await cancelSellOrder(request.payload, isFromExtension); - event.source.postMessage({ + case 'PUBLISH_MULTIPLE_QDN_RESOURCES': { + try { + const res = await publishMultipleQDNResources( + request.payload, + event.source, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "IS_USING_PUBLIC_NODE": { - try { - let isGateway = await isRunningGateway() - event.source.postMessage({ + case 'VOTE_ON_POLL': { + try { + const res = await voteOnPoll(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'CREATE_POLL': { + try { + const res = await createPoll(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'SEND_CHAT_MESSAGE': { + try { + const res = await sendChatMessage( + request.payload, + isFromExtension, + appInfo + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'JOIN_GROUP': { + try { + const res = await joinGroup(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'DEPLOY_AT': { + try { + const res = await deployAt(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_USER_WALLET': { + try { + const res = await getUserWallet( + request.payload, + isFromExtension, + appInfo + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_WALLET_BALANCE': { + try { + const res = await getWalletBalance( + request.payload, + false, + isFromExtension, + appInfo + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_USER_WALLET_TRANSACTIONS': { + try { + const res = await getUserWalletTransactions( + request.payload, + isFromExtension, + appInfo + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_USER_WALLET_INFO': { + try { + const res = await getUserWalletInfo( + request.payload, + isFromExtension, + appInfo + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_CROSSCHAIN_SERVER_INFO': { + try { + const res = await getCrossChainServerInfo(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_TX_ACTIVITY_SUMMARY': { + try { + const res = await getTxActivitySummary(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_FOREIGN_FEE': { + try { + const res = await getForeignFee(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'UPDATE_FOREIGN_FEE': { + try { + const res = await updateForeignFee(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_SERVER_CONNECTION_HISTORY': { + try { + const res = await getServerConnectionHistory(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'SET_CURRENT_FOREIGN_SERVER': { + try { + const res = await setCurrentForeignServer(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'ADD_FOREIGN_SERVER': { + try { + const res = await addForeignServer(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'REMOVE_FOREIGN_SERVER': { + try { + const res = await removeForeignServer(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_DAY_SUMMARY': { + try { + const res = await getDaySummary(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_NODE_INFO': { + try { + const res = await getNodeInfo(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'GET_NODE_STATUS': { + try { + const res = await getNodeStatus(request.payload); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'SEND_COIN': { + try { + const res = await sendCoin(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'CREATE_TRADE_BUY_ORDER': { + try { + const res = await createBuyOrder(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'CREATE_TRADE_SELL_ORDER': { + try { + const res = await createSellOrder(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'CANCEL_TRADE_SELL_ORDER': { + try { + const res = await cancelSellOrder(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + + case 'IS_USING_PUBLIC_NODE': { + try { + let isGateway = await isRunningGateway(); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: isGateway, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "ADMIN_ACTION": { - try { - const res = await adminAction(request.payload, isFromExtension) - event.source.postMessage({ + case 'ADMIN_ACTION': { + try { + const res = await adminAction(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "SIGN_TRANSACTION": { - try { - const res = await signTransaction(request.payload, isFromExtension) - event.source.postMessage({ + case 'SIGN_TRANSACTION': { + try { + const res = await signTransaction(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "OPEN_NEW_TAB": { - try { - const res = await openNewTab(request.payload, isFromExtension) - event.source.postMessage({ + case 'OPEN_NEW_TAB': { + try { + const res = await openNewTab(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CREATE_AND_COPY_EMBED_LINK": { - try { - const res = await createAndCopyEmbedLink(request.payload, isFromExtension) - event.source.postMessage({ + case 'CREATE_AND_COPY_EMBED_LINK': { + try { + const res = await createAndCopyEmbedLink( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "ENCRYPT_DATA_WITH_SHARING_KEY": { - try { - const res = await encryptDataWithSharingKey(request.payload, isFromExtension) - event.source.postMessage({ + case 'ENCRYPT_DATA_WITH_SHARING_KEY': { + try { + const res = await encryptDataWithSharingKey( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "DECRYPT_DATA_WITH_SHARING_KEY": { - try { - const res = await decryptDataWithSharingKey(request.payload, isFromExtension) - event.source.postMessage({ + case 'DECRYPT_DATA_WITH_SHARING_KEY': { + try { + const res = await decryptDataWithSharingKey( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "DELETE_HOSTED_DATA" : { - try { - const res = await deleteHostedData(request.payload, isFromExtension) - event.source.postMessage({ + case 'DELETE_HOSTED_DATA': { + try { + const res = await deleteHostedData(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } - case "GET_HOSTED_DATA" : { - try { - const res = await getHostedData(request.payload, isFromExtension) - event.source.postMessage({ + break; + } + case 'GET_HOSTED_DATA': { + try { + const res = await getHostedData(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "SHOW_ACTIONS" : { - try { - - event.source.postMessage({ + case 'SHOW_ACTIONS': { + try { + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: listOfAllQortalRequests, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "REGISTER_NAME" : { - try { - const res = await registerNameRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'REGISTER_NAME': { + try { + const res = await registerNameRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "UPDATE_NAME" : { - try { - const res = await updateNameRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'UPDATE_NAME': { + try { + const res = await updateNameRequest(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "LEAVE_GROUP" : { - try { - const res = await leaveGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'LEAVE_GROUP': { + try { + const res = await leaveGroupRequest(request.payload, isFromExtension); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "INVITE_TO_GROUP" : { - try { - const res = await inviteToGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'INVITE_TO_GROUP': { + try { + const res = await inviteToGroupRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "KICK_FROM_GROUP" : { - try { - const res = await kickFromGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'KICK_FROM_GROUP': { + try { + const res = await kickFromGroupRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "BAN_FROM_GROUP" : { - try { - const res = await banFromGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'BAN_FROM_GROUP': { + try { + const res = await banFromGroupRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CANCEL_GROUP_BAN" : { - try { - const res = await cancelGroupBanRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'CANCEL_GROUP_BAN': { + try { + const res = await cancelGroupBanRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "ADD_GROUP_ADMIN" : { - try { - const res = await addGroupAdminRequest(request.payload, isFromExtension) + case 'ADD_GROUP_ADMIN': { + try { + const res = await addGroupAdminRequest( + request.payload, + isFromExtension + ); - event.source.postMessage({ + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "DECRYPT_AESGCM" : { - try { - const res = await decryptAESGCMRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'DECRYPT_AESGCM': { + try { + const res = await decryptAESGCMRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "REMOVE_GROUP_ADMIN" : { - try { - const res = await removeGroupAdminRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'REMOVE_GROUP_ADMIN': { + try { + const res = await removeGroupAdminRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CANCEL_GROUP_INVITE" : { - try { - const res = await cancelGroupInviteRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'CANCEL_GROUP_INVITE': { + try { + const res = await cancelGroupInviteRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "CREATE_GROUP" : { - try { - const res = await createGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + case 'CREATE_GROUP': { + try { + const res = await createGroupRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } - case "UPDATE_GROUP" : { - try { - const res = await updateGroupRequest(request.payload, isFromExtension) - event.source.postMessage({ + break; + } + case 'UPDATE_GROUP': { + try { + const res = await updateGroupRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } + break; + } - case "GET_ARRR_SYNC_STATUS": { - try { - const res = await getArrrSyncStatus(request.payload); - event.source.postMessage({ + case 'GET_ARRR_SYNC_STATUS': { + try { + const res = await getArrrSyncStatus(request.payload); + event.source.postMessage( + { requestId: request.requestId, action: request.action, payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } - 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({ + 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({ + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { requestId: request.requestId, action: request.action, error: error?.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; + type: 'backgroundMessageResponse', + }, + event.origin + ); } - case "BUY_NAME": { - try { - const res = await buyNameRequest(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - case "SELL_NAME": { - try { - const res = await sellNameRequest(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - type: "backgroundMessageResponse", - }, event.origin); - } catch (error) { - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - error: error.message, - type: "backgroundMessageResponse", - }, event.origin); - } - break; - } - case "CANCEL_SELL_NAME": { - try { - const res = await cancelSellNameRequest(request.payload, isFromExtension); - event.source.postMessage({ - requestId: request.requestId, - action: request.action, - payload: res, - 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; + break; } - }); - } - - // Initialize the message listener - setupMessageListenerQortalRequest(); + case 'BUY_NAME': { + try { + const res = await buyNameRequest(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + case 'SELL_NAME': { + try { + const res = await sellNameRequest(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + case 'CANCEL_SELL_NAME': { + try { + const res = await cancelSellNameRequest( + request.payload, + isFromExtension + ); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + 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; + } + }); +} + +// Initialize the message listener +setupMessageListenerQortalRequest(); diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index c0206b2..a160305 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1,6 +1,5 @@ -import { Sha256 } from "asmcrypto.js"; +import { Sha256 } from 'asmcrypto.js'; import { - computePow, createEndpoint, getBalanceInfo, getFee, @@ -8,12 +7,10 @@ import { getLastRef, getSaveWallet, processTransactionVersion2, - removeDuplicateWindow, signChatFunc, joinGroup as joinGroupFunc, sendQortFee, sendCoin as sendCoinFunc, - isUsingLocal, createBuyOrderTx, performPowTask, parseErrorResponse, @@ -35,18 +32,24 @@ import { cancelSellName, buyName, getBaseApi, -} from "../background"; -import { getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption"; -import { showSaveFilePicker } from "../components/Apps/useQortalMessageListener"; -import { getPublishesFromAdminsAdminSpace } from "../components/Chat/AdminSpaceInner"; -import { extractComponents } from "../components/Chat/MessageDisplay"; -import { decryptResource, getGroupAdmins, getPublishesFromAdmins, validateSecretKey } from "../components/Group/Group"; -import { QORT_DECIMALS } from "../constants/constants"; -import Base58 from "../deps/Base58"; -import ed2curve from "../deps/ed2curve"; -import nacl from "../deps/nacl-fast"; - - +} from '../background'; +import { + getNameInfo, + uint8ArrayToObject, +} from '../backgroundFunctions/encryption'; +import { showSaveFilePicker } from '../components/Apps/useQortalMessageListener'; +import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner'; +import { extractComponents } from '../components/Chat/MessageDisplay'; +import { + decryptResource, + getGroupAdmins, + getPublishesFromAdmins, + validateSecretKey, +} from '../components/Group/Group'; +import { QORT_DECIMALS } from '../constants/constants'; +import Base58 from '../deps/Base58'; +import ed2curve from '../deps/ed2curve'; +import nacl from '../deps/nacl-fast'; import { base64ToUint8Array, createSymmetricKeyAndNonce, @@ -59,49 +62,49 @@ import { objectToBase64, uint8ArrayStartsWith, uint8ArrayToBase64, -} from "../qdn/encryption/group-encryption"; -import { publishData } from "../qdn/publish/pubish"; +} from '../qdn/encryption/group-encryption'; +import { publishData } from '../qdn/publish/pubish'; import { getPermission, isRunningGateway, setPermission, -} from "../qortalRequests"; -import TradeBotCreateRequest from "../transactions/TradeBotCreateRequest"; -import DeleteTradeOffer from "../transactions/TradeBotDeleteRequest"; -import signTradeBotTransaction from "../transactions/signTradeBotTransaction"; -import { createTransaction } from "../transactions/transactions"; -import { executeEvent } from "../utils/events"; -import { fileToBase64 } from "../utils/fileReading"; -import { mimeToExtensionMap } from "../utils/memeTypes"; -import { RequestQueueWithPromise } from "../utils/queue/queue"; -import utils from "../utils/utils"; +} from '../qortalRequests'; +import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest'; +import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest'; +import signTradeBotTransaction from '../transactions/signTradeBotTransaction'; +import { createTransaction } from '../transactions/transactions'; +import { executeEvent } from '../utils/events'; +import { fileToBase64 } from '../utils/fileReading'; +import { mimeToExtensionMap } from '../utils/memeTypes'; +import { RequestQueueWithPromise } from '../utils/queue/queue'; +import utils from '../utils/utils'; export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); const sellerForeignFee = { LITECOIN: { - value: "~0.00005", - ticker: "LTC", + value: '~0.00005', + ticker: 'LTC', }, DOGECOIN: { - value: "~0.005", - ticker: "DOGE", + value: '~0.005', + ticker: 'DOGE', }, BITCOIN: { - value: "~0.0001", - ticker: "BTC", + value: '~0.0001', + ticker: 'BTC', }, DIGIBYTE: { - value: "~0.0005", - ticker: "DGB", + value: '~0.0005', + ticker: 'DGB', }, RAVENCOIN: { - value: "~0.006", - ticker: "RVN", + value: '~0.006', + ticker: 'RVN', }, PIRATECHAIN: { - value: "~0.0002", - ticker: "ARRR", + value: '~0.0002', + ticker: 'ARRR', }, }; @@ -113,24 +116,28 @@ const rvnFeePerByte = 0.00001125; const MAX_RETRIES = 3; // Set max number of retries - -export async function retryTransaction(fn, args, throwError, retries = MAX_RETRIES) { +export async function retryTransaction( + fn, + args, + throwError, + retries = MAX_RETRIES +) { let attempt = 0; while (attempt < retries) { try { - return await fn(...args); + 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") + console.error('Max retries reached. Skipping transaction.'); + if (throwError) { + throw new Error(error?.message || 'Unable to process transaction'); } else { - throw new Error(error?.message || "Unable to process transaction") + throw new Error(error?.message || 'Unable to process transaction'); } } - await new Promise(res => setTimeout(res, 10000)); + await new Promise((res) => setTimeout(res, 10000)); } } } @@ -142,23 +149,24 @@ function roundUpToDecimals(number, decimals = 8) { export const _createPoll = async ( { pollName, pollDescription, options }, - isFromExtension, skipPermission + isFromExtension, + skipPermission ) => { - const fee = await getFee("CREATE_POLL"); - let resPermission = {} - if(!skipPermission){ - resPermission = await getUserPermission( + const fee = await getFee('CREATE_POLL'); + let resPermission = {}; + if (!skipPermission) { + resPermission = await getUserPermission( { - text1: "You are requesting to create the poll below:", + text1: 'You are requesting to create the poll below:', text2: `Poll: ${pollName}`, text3: `Description: ${pollDescription}`, - text4: `Options: ${options?.join(", ")}`, + text4: `Options: ${options?.join(', ')}`, fee: fee.fee, }, isFromExtension ); } - + const { accepted = false } = resPermission; if (accepted || skipPermission) { @@ -186,11 +194,11 @@ export const _createPoll = async ( const res = await processTransactionVersion2(signedBytes); if (!res?.signature) throw new Error( - res?.message || "Transaction was not able to be processed" + res?.message || 'Transaction was not able to be processed' ); return res; } else { - throw new Error("User declined request"); + throw new Error('User declined request'); } }; @@ -198,11 +206,11 @@ const _deployAt = async ( { name, description, tags, creationBytes, amount, assetId, atType }, isFromExtension ) => { - const fee = await getFee("DEPLOY_AT"); + const fee = await getFee('DEPLOY_AT'); const resPermission = await getUserPermission( { - text1: "Would you like to deploy this AT?", + text1: 'Would you like to deploy this AT?', text2: `Name: ${name}`, text3: `Description: ${description}`, fee: fee.fee, @@ -242,24 +250,25 @@ const _deployAt = async ( const res = await processTransactionVersion2(signedBytes); if (!res?.signature) throw new Error( - res?.message || "Transaction was not able to be processed" + res?.message || 'Transaction was not able to be processed' ); return res; } else { - throw new Error("User declined transaction"); + throw new Error('User declined transaction'); } }; export const _voteOnPoll = async ( { pollName, optionIndex, optionName }, - isFromExtension, skipPermission + isFromExtension, + skipPermission ) => { - const fee = await getFee("VOTE_ON_POLL"); - let resPermission = {} - if(!skipPermission){ + const fee = await getFee('VOTE_ON_POLL'); + let resPermission = {}; + if (!skipPermission) { resPermission = await getUserPermission( { - text1: "You are being requested to vote on the poll below:", + text1: 'You are being requested to vote on the poll below:', text2: `Poll: ${pollName}`, text3: `Option: ${optionName}`, fee: fee.fee, @@ -267,7 +276,7 @@ export const _voteOnPoll = async ( isFromExtension ); } - + const { accepted = false } = resPermission; if (accepted || skipPermission) { @@ -294,11 +303,11 @@ export const _voteOnPoll = async ( const res = await processTransactionVersion2(signedBytes); if (!res?.signature) throw new Error( - res?.message || "Transaction was not able to be processed" + res?.message || 'Transaction was not able to be processed' ); return res; } else { - throw new Error("User declined request"); + throw new Error('User declined request'); } }; @@ -309,7 +318,7 @@ const handleFileMessage = (event) => { const { action, requestId, result, error } = event.data; if ( - action === "getFileFromIndexedDBResponse" && + action === 'getFileFromIndexedDBResponse' && fileRequestResolvers.has(requestId) ) { const { resolve, reject } = fileRequestResolvers.get(requestId); @@ -318,12 +327,12 @@ const handleFileMessage = (event) => { if (result) { resolve(result); } else { - reject(error || "Failed to retrieve file"); + reject(error || 'Failed to retrieve file'); } } }; -window.addEventListener("message", handleFileMessage); +window.addEventListener('message', handleFileMessage); function getFileFromContentScript(fileId) { return new Promise((resolve, reject) => { @@ -334,14 +343,14 @@ function getFileFromContentScript(fileId) { // Send the request message window.postMessage( - { action: "getFileFromIndexedDB", fileId, requestId }, + { action: 'getFileFromIndexedDB', fileId, requestId }, targetOrigin ); // Timeout to handle no response scenario setTimeout(() => { if (fileRequestResolvers.has(requestId)) { - fileRequestResolvers.get(requestId).reject("Request timed out"); + fileRequestResolvers.get(requestId).reject('Request timed out'); fileRequestResolvers.delete(requestId); // Clean up on timeout } }, 10000); // 10-second timeout @@ -362,7 +371,7 @@ const handleMessage = (event) => { // Check if this is the expected response action and if we have a stored resolver if ( - action === "QORTAL_REQUEST_PERMISSION_RESPONSE" && + action === 'QORTAL_REQUEST_PERMISSION_RESPONSE' && responseResolvers.has(requestId) ) { // Resolve the stored promise with the result @@ -371,7 +380,7 @@ const handleMessage = (event) => { } }; -window.addEventListener("message", handleMessage); +window.addEventListener('message', handleMessage); async function getUserPermission(payload, isFromExtension) { return new Promise((resolve) => { @@ -382,7 +391,7 @@ async function getUserPermission(payload, isFromExtension) { // Send the request message window.postMessage( { - action: "QORTAL_REQUEST_PERMISSION", + action: 'QORTAL_REQUEST_PERMISSION', payload, requestId, isFromExtension, @@ -400,7 +409,11 @@ async function getUserPermission(payload, isFromExtension) { }); } -export const getUserAccount = async ({ isFromExtension, appInfo, skipAuth }) => { +export const getUserAccount = async ({ + isFromExtension, + appInfo, + skipAuth, +}) => { try { const value = (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false; @@ -408,17 +421,17 @@ export const getUserAccount = async ({ isFromExtension, appInfo, skipAuth }) => if (value) { skip = true; } - if(skipAuth){ - skip = true + if (skipAuth) { + skip = true; } let resPermission; if (!skip) { resPermission = await getUserPermission( { - text1: "Do you give this application permission to authenticate?", + text1: 'Do you give this application permission to authenticate?', checkbox1: { value: false, - label: "Always authenticate automatically", + label: 'Always authenticate automatically', }, }, isFromExtension @@ -438,10 +451,10 @@ export const getUserAccount = async ({ isFromExtension, appInfo, skipAuth }) => publicKey, }; } else { - throw new Error("User declined request"); + throw new Error('User declined request'); } } catch (error) { - throw new Error("Unable to fetch user account"); + throw new Error('Unable to fetch user account'); } }; @@ -452,7 +465,7 @@ export const encryptData = async (data, sender) => { data64 = await fileToBase64(data?.file || data?.blob); } if (!data64) { - throw new Error("Please include data to encrypt"); + throw new Error('Please include data to encrypt'); } const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; @@ -468,195 +481,203 @@ export const encryptData = async (data, sender) => { if (encryptDataResponse) { return encryptDataResponse; } else { - throw new Error("Unable to encrypt"); + throw new Error('Unable to encrypt'); } }; export const encryptQortalGroupData = async (data, sender) => { let data64 = data?.data64 || data?.base64; - let groupId = data?.groupId - let isAdmins = data?.isAdmins - if(!groupId){ - throw new Error('Please provide a groupId') + let groupId = data?.groupId; + let isAdmins = data?.isAdmins; + if (!groupId) { + throw new Error('Please provide a groupId'); } if (data?.file || data?.blob) { data64 = await fileToBase64(data?.file || data?.blob); } if (!data64) { - throw new Error("Please include data to encrypt"); + throw new Error('Please include data to encrypt'); } - - let secretKeyObject - if(!isAdmins){ - if(groupSecretkeys[groupId] && groupSecretkeys[groupId].secretKeyObject && groupSecretkeys[groupId]?.timestamp && (Date.now() - groupSecretkeys[groupId]?.timestamp) < 1200000){ - secretKeyObject = groupSecretkeys[groupId].secretKeyObject - } - - if(!secretKeyObject){ - const { names } = - await getGroupAdmins(groupId) - - const publish = - await getPublishesFromAdmins(names, groupId); - if(publish === false) throw new Error('No group key found.') - const url = await createEndpoint(`/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true`); - - const res = await fetch( -url - ); - const resData = await res.text(); - - const decryptedKey: any = await decryptResource(resData, true); - - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); - secretKeyObject = decryptedKeyToObject - groupSecretkeys[groupId] = { - secretKeyObject, - timestamp: Date.now() + let secretKeyObject; + if (!isAdmins) { + if ( + groupSecretkeys[groupId] && + groupSecretkeys[groupId].secretKeyObject && + groupSecretkeys[groupId]?.timestamp && + Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[groupId].secretKeyObject; } - } -} else { - if(groupSecretkeys[`admins-${groupId}`] && groupSecretkeys[`admins-${groupId}`].secretKeyObject && groupSecretkeys[`admins-${groupId}`]?.timestamp && (Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp) < 1200000){ - secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject - } + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); - if(!secretKeyObject){ - const { names } = - await getGroupAdmins(groupId) + const publish = await getPublishesFromAdmins(names, groupId); + if (publish === false) throw new Error('No group key found.'); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); - const publish = - await getPublishesFromAdminsAdminSpace(names, groupId); - if(publish === false) throw new Error('No group key found.') - const url = await createEndpoint(`/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true`); + const res = await fetch(url); + const resData = await res.text(); - const res = await fetch( -url - ); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + const decryptedKey: any = await decryptResource(resData, true); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); - secretKeyObject = decryptedKeyToObject - groupSecretkeys[`admins-${groupId}`] = { - secretKeyObject, - timestamp: Date.now() + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error('SecretKey is not valid'); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[groupId] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } else { + if ( + groupSecretkeys[`admins-${groupId}`] && + groupSecretkeys[`admins-${groupId}`].secretKeyObject && + groupSecretkeys[`admins-${groupId}`]?.timestamp && + Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; + } + + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdminsAdminSpace(names, groupId); + if (publish === false) throw new Error('No group key found.'); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error('SecretKey is not valid'); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[`admins-${groupId}`] = { + secretKeyObject, + timestamp: Date.now(), + }; } } + const resGroupEncryptedResource = encryptSingle({ + data64, + secretKeyObject: secretKeyObject, + }); - -} - - const resGroupEncryptedResource = encryptSingle({ - data64, secretKeyObject: secretKeyObject, - }) - if (resGroupEncryptedResource) { return resGroupEncryptedResource; } else { - throw new Error("Unable to encrypt"); + throw new Error('Unable to encrypt'); } }; export const decryptQortalGroupData = async (data, sender) => { let data64 = data?.data64 || data?.base64; - let groupId = data?.groupId - let isAdmins = data?.isAdmins - if(!groupId){ - throw new Error('Please provide a groupId') + let groupId = data?.groupId; + let isAdmins = data?.isAdmins; + if (!groupId) { + throw new Error('Please provide a groupId'); } if (!data64) { - throw new Error("Please include data to encrypt"); + throw new Error('Please include data to encrypt'); } - let secretKeyObject - if(!isAdmins){ - if(groupSecretkeys[groupId] && groupSecretkeys[groupId].secretKeyObject && groupSecretkeys[groupId]?.timestamp && (Date.now() - groupSecretkeys[groupId]?.timestamp) < 1200000){ - secretKeyObject = groupSecretkeys[groupId].secretKeyObject - } - if(!secretKeyObject){ - const { names } = - await getGroupAdmins(groupId) - - const publish = - await getPublishesFromAdmins(names, groupId); - if(publish === false) throw new Error('No group key found.') - const url = await createEndpoint(`/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true`); - - const res = await fetch( -url - ); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); - - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); - secretKeyObject = decryptedKeyToObject - groupSecretkeys[groupId] = { - secretKeyObject, - timestamp: Date.now() + let secretKeyObject; + if (!isAdmins) { + if ( + groupSecretkeys[groupId] && + groupSecretkeys[groupId].secretKeyObject && + groupSecretkeys[groupId]?.timestamp && + Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[groupId].secretKeyObject; } - } -} else { - if(groupSecretkeys[`admins-${groupId}`] && groupSecretkeys[`admins-${groupId}`].secretKeyObject && groupSecretkeys[`admins-${groupId}`]?.timestamp && (Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp) < 1200000){ - secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject - } - if(!secretKeyObject){ - const { names } = - await getGroupAdmins(groupId) + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); - const publish = - await getPublishesFromAdminsAdminSpace(names, groupId); - if(publish === false) throw new Error('No group key found.') - const url = await createEndpoint(`/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true`); + const publish = await getPublishesFromAdmins(names, groupId); + if (publish === false) throw new Error('No group key found.'); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); - const res = await fetch( -url - ); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); - secretKeyObject = decryptedKeyToObject - groupSecretkeys[`admins-${groupId}`] = { - secretKeyObject, - timestamp: Date.now() + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error('SecretKey is not valid'); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[groupId] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } else { + if ( + groupSecretkeys[`admins-${groupId}`] && + groupSecretkeys[`admins-${groupId}`].secretKeyObject && + groupSecretkeys[`admins-${groupId}`]?.timestamp && + Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; + } + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdminsAdminSpace(names, groupId); + if (publish === false) throw new Error('No group key found.'); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); + + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error('SecretKey is not valid'); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[`admins-${groupId}`] = { + secretKeyObject, + timestamp: Date.now(), + }; } } - -} - - const resGroupDecryptResource = decryptSingle({ - data64, secretKeyObject: secretKeyObject, skipDecodeBase64: true - }) + const resGroupDecryptResource = decryptSingle({ + data64, + secretKeyObject: secretKeyObject, + skipDecodeBase64: true, + }); if (resGroupDecryptResource) { return resGroupDecryptResource; } else { - throw new Error("Unable to decrypt"); + throw new Error('Unable to decrypt'); } }; @@ -667,14 +688,14 @@ export const encryptDataWithSharingKey = async (data, sender) => { data64 = await fileToBase64(data?.file || data?.blob); } if (!data64) { - throw new Error("Please include data to encrypt"); + throw new Error('Please include data to encrypt'); } - const symmetricKey = createSymmetricKeyAndNonce() + const symmetricKey = createSymmetricKeyAndNonce(); const dataObject = { data: data64, - key:symmetricKey.messageKey - } - const dataObjectBase64 = await objectToBase64(dataObject) + key: symmetricKey.messageKey, + }; + const dataObjectBase64 = await objectToBase64(dataObject); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; @@ -686,70 +707,70 @@ export const encryptDataWithSharingKey = async (data, sender) => { publicKeys: publicKeys, privateKey, userPublicKey, - customSymmetricKey: symmetricKey.messageKey + customSymmetricKey: symmetricKey.messageKey, }); if (encryptDataResponse) { return encryptDataResponse; } else { - throw new Error("Unable to encrypt"); + throw new Error('Unable to encrypt'); } }; export const decryptDataWithSharingKey = async (data, sender) => { const { encryptedData, key } = data; - if (!encryptedData) { - throw new Error("Please include data to decrypt"); + throw new Error('Please include data to decrypt'); } - const decryptedData = await decryptGroupEncryptionWithSharingKey({data64EncryptedData: encryptedData, key}) - const base64ToObject = JSON.parse(atob(decryptedData)) - if(!base64ToObject.data) throw new Error('No data in the encrypted resource') - return base64ToObject.data + const decryptedData = await decryptGroupEncryptionWithSharingKey({ + data64EncryptedData: encryptedData, + key, + }); + const base64ToObject = JSON.parse(atob(decryptedData)); + if (!base64ToObject.data) + throw new Error('No data in the encrypted resource'); + return base64ToObject.data; }; export const getHostedData = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a public node"); + throw new Error('This action cannot be done through a public node'); } const resPermission = await getUserPermission( { - text1: "Do you give this application permission to", + text1: 'Do you give this application permission to', text2: `Get a list of your hosted data?`, }, isFromExtension ); const { accepted } = resPermission; - if(accepted){ + if (accepted) { const limit = data?.limit ? data?.limit : 20; - const query = data?.query ? data?.query : "" - const offset = data?.offset ? data?.offset : 0 + const query = data?.query ? data?.query : ''; + const offset = data?.offset ? data?.offset : 0; - let urlPath = `/arbitrary/hosted/resources/?limit=${limit}&offset=${offset}` - if(query){ - urlPath = urlPath + `&query=${query}` + let urlPath = `/arbitrary/hosted/resources/?limit=${limit}&offset=${offset}`; + if (query) { + urlPath = urlPath + `&query=${query}`; } - - const url = await createEndpoint(urlPath); - const response = await fetch(url); - const dataResponse = await response.json(); - return dataResponse - - } else { - throw new Error("User declined to get list of hosted resources"); + const url = await createEndpoint(urlPath); + const response = await fetch(url); + const dataResponse = await response.json(); + return dataResponse; + } else { + throw new Error('User declined to get list of hosted resources'); } - }; export const deleteHostedData = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a public node"); + throw new Error('This action cannot be done through a public node'); } - const requiredFields = ["hostedData"]; + const requiredFields = ['hostedData']; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { @@ -758,35 +779,36 @@ export const deleteHostedData = async (data, isFromExtension) => { }); const resPermission = await getUserPermission( { - text1: "Do you give this application permission to", + text1: 'Do you give this application permission to', text2: `Delete ${data?.hostedData?.length} hosted resources?`, }, isFromExtension ); const { accepted } = resPermission; - if(accepted){ + if (accepted) { const { hostedData } = data; - for (const hostedDataItem of hostedData){ - try { - const url = await createEndpoint(`/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}`); - await fetch(url, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - } - }); - } catch (error) { - //error + for (const hostedDataItem of hostedData) { + try { + const url = await createEndpoint( + `/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}` + ); + await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + //error + } } - } - return true + return true; } else { - throw new Error("User declined delete hosted resources"); + throw new Error('User declined delete hosted resources'); } - }; export const decryptData = async (data) => { const { encryptedData, publicKey } = data; @@ -800,7 +822,7 @@ export const decryptData = async (data) => { const uint8Array = base64ToUint8Array(encryptedData); const startsWithQortalEncryptedData = uint8ArrayStartsWith( uint8Array, - "qortalEncryptedData" + 'qortalEncryptedData' ); if (startsWithQortalEncryptedData) { if (!publicKey) { @@ -816,7 +838,7 @@ export const decryptData = async (data) => { } const startsWithQortalGroupEncryptedData = uint8ArrayStartsWith( uint8Array, - "qortalGroupEncryptedData" + 'qortalGroupEncryptedData' ); if (startsWithQortalGroupEncryptedData) { const decryptedData = decryptGroupDataQortalRequest( @@ -826,15 +848,15 @@ export const decryptData = async (data) => { const decryptedDataToBase64 = uint8ArrayToBase64(decryptedData); return decryptedDataToBase64; } - throw new Error("Unable to decrypt"); + throw new Error('Unable to decrypt'); }; export const getListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a public node"); + throw new Error('This action cannot be done through a public node'); } - const requiredFields = ["list_name"]; + const requiredFields = ['list_name']; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { @@ -842,11 +864,11 @@ export const getListItems = async (data, isFromExtension) => { } }); if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } - const value = (await getPermission("qAPPAutoLists")) || false; + const value = (await getPermission('qAPPAutoLists')) || false; let skip = false; if (value) { @@ -858,12 +880,12 @@ export const getListItems = async (data, isFromExtension) => { if (!skip) { resPermission = await getUserPermission( { - text1: "Do you give this application permission to", - text2: "Access the list", + text1: 'Do you give this application permission to', + text2: 'Access the list', highlightedText: data.list_name, checkbox1: { value: value, - label: "Always allow lists to be retrieved automatically", + label: 'Always allow lists to be retrieved automatically', }, }, isFromExtension @@ -871,27 +893,27 @@ export const getListItems = async (data, isFromExtension) => { const { accepted, checkbox1 } = resPermission; acceptedVar = accepted; checkbox1Var = checkbox1; - setPermission("qAPPAutoLists", checkbox1); + setPermission('qAPPAutoLists', checkbox1); } if (acceptedVar || skip) { const url = await createEndpoint(`/lists/${data.list_name}`); const response = await fetch(url); - if (!response.ok) throw new Error("Failed to fetch"); + if (!response.ok) throw new Error('Failed to fetch'); const list = await response.json(); return list; } else { - throw new Error("User declined to share list"); + throw new Error('User declined to share list'); } }; export const addListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a public node"); + throw new Error('This action cannot be done through a public node'); } - const requiredFields = ["list_name", "items"]; + const requiredFields = ['list_name', 'items']; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { @@ -899,7 +921,7 @@ export const addListItems = async (data, isFromExtension) => { } }); if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } @@ -909,9 +931,9 @@ export const addListItems = async (data, isFromExtension) => { const resPermission = await getUserPermission( { - text1: "Do you give this application permission to", + text1: 'Do you give this application permission to', text2: `Add the following to the list ${list_name}:`, - highlightedText: items.join(", "), + highlightedText: items.join(', '), }, isFromExtension ); @@ -924,14 +946,14 @@ export const addListItems = async (data, isFromExtension) => { }; const bodyToString = JSON.stringify(body); const response = await fetch(url, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: bodyToString, }); - if (!response.ok) throw new Error("Failed to add to list"); + if (!response.ok) throw new Error('Failed to add to list'); let res; try { res = await response.clone().json(); @@ -940,16 +962,16 @@ export const addListItems = async (data, isFromExtension) => { } return res; } else { - throw new Error("User declined add to list"); + throw new Error('User declined add to list'); } }; export const deleteListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a public node"); + throw new Error('This action cannot be done through a public node'); } - const requiredFields = ["list_name"]; + const requiredFields = ['list_name']; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { @@ -957,20 +979,20 @@ export const deleteListItems = async (data, isFromExtension) => { } }); if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } - if(!data?.item && !data?.items){ - throw new Error('Missing fields: items') + if (!data?.item && !data?.items) { + throw new Error('Missing fields: items'); } const item = data?.item; - const items = data?.items + const items = data?.items; const list_name = data.list_name; const resPermission = await getUserPermission( { - text1: "Do you give this application permission to", + text1: 'Do you give this application permission to', text2: `Remove the following from the list ${list_name}:`, highlightedText: items ? JSON.stringify(items) : item, }, @@ -985,14 +1007,14 @@ export const deleteListItems = async (data, isFromExtension) => { }; const bodyToString = JSON.stringify(body); const response = await fetch(url, { - method: "DELETE", + method: 'DELETE', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: bodyToString, }); - if (!response.ok) throw new Error("Failed to add to list"); + if (!response.ok) throw new Error('Failed to add to list'); let res; try { res = await response.clone().json(); @@ -1001,7 +1023,7 @@ export const deleteListItems = async (data, isFromExtension) => { } return res; } else { - throw new Error("User declined delete from list"); + throw new Error('User declined delete from list'); } }; @@ -1010,7 +1032,7 @@ export const publishQDNResource = async ( sender, isFromExtension ) => { - const requiredFields = ["service"]; + const requiredFields = ['service']; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { @@ -1018,25 +1040,25 @@ export const publishQDNResource = async ( } }); if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } if (!data.file && !data.data64 && !data.base64) { - throw new Error("No data or file was submitted"); + throw new Error('No data or file was submitted'); } // Use "default" if user hasn't specified an identifier const service = data.service; - const appFee = data?.appFee ? +data.appFee : undefined - const appFeeRecipient = data?.appFeeRecipient - let hasAppFee = false - if(appFee && appFee > 0 && appFeeRecipient){ - hasAppFee = true + const appFee = data?.appFee ? +data.appFee : undefined; + const appFeeRecipient = data?.appFeeRecipient; + let hasAppFee = false; + if (appFee && appFee > 0 && appFeeRecipient) { + hasAppFee = true; } const registeredName = await getNameInfo(); const name = registeredName; - if(!name){ - throw new Error('User has no Qortal name') + if (!name) { + throw new Error('User has no Qortal name'); } let identifier = data.identifier; let data64 = data.data64 || data.base64; @@ -1046,18 +1068,18 @@ export const publishQDNResource = async ( const category = data.category; const tags = data?.tags || []; -const result = {}; + const result = {}; -// Fill tags dynamically while maintaining backward compatibility -for (let i = 0; i < 5; i++) { - result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; -} + // Fill tags dynamically while maintaining backward compatibility + for (let i = 0; i < 5; i++) { + result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; + } -// Access tag1 to tag5 from result -const { tag1, tag2, tag3, tag4, tag5 } = result; + // Access tag1 to tag5 from result + const { tag1, tag2, tag3, tag4, tag5 } = result; if (data.identifier == null) { - identifier = "default"; + identifier = 'default'; } if (data?.file || data?.blob) { data64 = await fileToBase64(data?.file || data?.blob); @@ -1067,10 +1089,9 @@ const { tag1, tag2, tag3, tag4, tag5 } = result; (!data.publicKeys || (Array.isArray(data.publicKeys) && data.publicKeys.length === 0)) ) { - throw new Error("Encrypting data requires public keys"); + throw new Error('Encrypting data requires public keys'); } - if (data.encrypt) { try { const resKeyPair = await getKeyPair(); @@ -1088,46 +1109,45 @@ const { tag1, tag2, tag3, tag4, tag5 } = result; } } catch (error) { throw new Error( - error.message || "Upload failed due to failed encryption" + error.message || 'Upload failed due to failed encryption' ); } } - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); - const handleDynamicValues = {} - if(hasAppFee){ - const feePayment = await getFee("PAYMENT"); + const handleDynamicValues = {}; + if (hasAppFee) { + const feePayment = await getFee('PAYMENT'); - handleDynamicValues['appFee'] = +appFee + +feePayment.fee, - handleDynamicValues['checkbox1'] = { - value: true, - label: "accept app fee", - } + (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), + (handleDynamicValues['checkbox1'] = { + value: true, + label: 'accept app fee', + }); } - if(!!data?.encrypt){ - handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}` + if (!!data?.encrypt) { + handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; } const resPermission = await getUserPermission( { - text1: "Do you give this application permission to publish to QDN?", + text1: 'Do you give this application permission to publish to QDN?', text2: `service: ${service}`, text3: `identifier: ${identifier || null}`, fee: fee.fee, - ...handleDynamicValues + ...handleDynamicValues, }, isFromExtension ); const { accepted, checkbox1 = false } = resPermission; if (accepted) { - try { const resPublish = await publishData({ registeredName: encodeURIComponent(name), file: data64, service: service, identifier: encodeURIComponent(identifier), - uploadType: "file", + uploadType: 'file', isBase64: true, filename: filename, title, @@ -1141,18 +1161,21 @@ const { tag1, tag2, tag3, tag4, tag5 } = result; apiVersion: 2, withFee: true, }); - if(resPublish?.signature && hasAppFee && checkbox1){ - sendCoinFunc({ - amount: appFee, - receiver: appFeeRecipient - }, true) + if (resPublish?.signature && hasAppFee && checkbox1) { + sendCoinFunc( + { + amount: appFee, + receiver: appFeeRecipient, + }, + true + ); } return resPublish; } catch (error) { - throw new Error(error?.message || "Upload failed"); + throw new Error(error?.message || 'Upload failed'); } } else { - throw new Error("User declined request"); + throw new Error('User declined request'); } }; @@ -1162,9 +1185,9 @@ export const checkArrrSyncStatus = async (seed) => { while (tries < 36) { const response = await fetch(_url, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: seed, }); @@ -1176,7 +1199,7 @@ export const checkArrrSyncStatus = async (seed) => { res = await response.text(); } - if (res.indexOf('<') > -1 || res !== "Synchronized") { + if (res.indexOf('<') > -1 || res !== 'Synchronized') { // Wait 2 seconds before trying again await new Promise((resolve) => setTimeout(resolve, 2000)); tries += 1; @@ -1187,16 +1210,15 @@ export const checkArrrSyncStatus = async (seed) => { } // If we exceed 6 tries, throw an error - throw new Error("Failed to synchronize after 36 attempts"); + throw new Error('Failed to synchronize after 36 attempts'); }; - export const publishMultipleQDNResources = async ( data: any, sender, isFromExtension ) => { - const requiredFields = ["resources"]; + const requiredFields = ['resources']; const missingFields: string[] = []; let feeAmount = null; requiredFields.forEach((field) => { @@ -1205,32 +1227,32 @@ export const publishMultipleQDNResources = async ( } }); if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } const resources = data.resources; if (!Array.isArray(resources)) { - throw new Error("Invalid data"); + throw new Error('Invalid data'); } if (resources.length === 0) { - throw new Error("No resources to publish"); + throw new Error('No resources to publish'); } - const encrypt = data?.encrypt + const encrypt = data?.encrypt; for (const resource of resources) { - const resourceEncrypt = encrypt && resource?.disableEncrypt !== true - if (!resourceEncrypt && resource?.service.endsWith("_PRIVATE")) { - const errorMsg = "Only encrypted data can go into private services"; - throw new Error(errorMsg) - } else if(resourceEncrypt && !resource?.service.endsWith("_PRIVATE")){ - const errorMsg = "For an encrypted publish please use a service that ends with _PRIVATE"; - throw new Error(errorMsg) + const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; + if (!resourceEncrypt && resource?.service.endsWith('_PRIVATE')) { + const errorMsg = 'Only encrypted data can go into private services'; + throw new Error(errorMsg); + } else if (resourceEncrypt && !resource?.service.endsWith('_PRIVATE')) { + const errorMsg = + 'For an encrypted publish please use a service that ends with _PRIVATE'; + throw new Error(errorMsg); } } - - + // if ( // data.encrypt && // (!data.publicKeys || @@ -1238,35 +1260,35 @@ export const publishMultipleQDNResources = async ( // ) { // throw new Error("Encrypting data requires public keys"); // } - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); const registeredName = await getNameInfo(); const name = registeredName; - if(!name){ - throw new Error('You need a Qortal name to publish.') + if (!name) { + throw new Error('You need a Qortal name to publish.'); } - const appFee = data?.appFee ? +data.appFee : undefined - const appFeeRecipient = data?.appFeeRecipient - let hasAppFee = false - if(appFee && appFee > 0 && appFeeRecipient){ - hasAppFee = true + const appFee = data?.appFee ? +data.appFee : undefined; + const appFeeRecipient = data?.appFeeRecipient; + let hasAppFee = false; + if (appFee && appFee > 0 && appFeeRecipient) { + hasAppFee = true; } - const handleDynamicValues = {} - if(hasAppFee){ - const feePayment = await getFee("PAYMENT"); + const handleDynamicValues = {}; + if (hasAppFee) { + const feePayment = await getFee('PAYMENT'); - handleDynamicValues['appFee'] = +appFee + +feePayment.fee, - handleDynamicValues['checkbox1'] = { - value: true, - label: "accept app fee", - } + (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), + (handleDynamicValues['checkbox1'] = { + value: true, + label: 'accept app fee', + }); } - if(data?.encrypt){ - handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}` + if (data?.encrypt) { + handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; } const resPermission = await getUserPermission( { - text1: "Do you give this application permission to publish to QDN?", + text1: 'Do you give this application permission to publish to QDN?', html: `