From 4de4f58498893e42481b8c832f9c8c154f422128 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 20 Jan 2025 19:45:46 +0200 Subject: [PATCH] fixes --- .../Apps/useQortalMessageListener.tsx | 40 +- src/components/Group/Group.tsx | 15 +- .../Group/ListOfGroupPromotions.tsx | 619 +++++++++--------- src/components/Group/ManageMembers.tsx | 30 +- src/qortalRequests.ts | 21 + src/qortalRequests/get.ts | 44 +- 6 files changed, 432 insertions(+), 337 deletions(-) diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 83bd708..68c7fa1 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -173,6 +173,44 @@ export function openIndexedDB() { }); } + export const listOfAllQortalRequests = [ + 'GET_USER_ACCOUNT', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', + 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL', + 'SEND_CHAT_MESSAGE', 'JOIN_GROUP', 'DEPLOY_AT', 'GET_USER_WALLET', + 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', + 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', + 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', + 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'PUBLISH_MULTIPLE_QDN_RESOURCES', + 'PUBLISH_QDN_RESOURCE', + 'ENCRYPT_DATA', + 'ENCRYPT_DATA_WITH_SHARING_KEY', + 'ENCRYPT_QORTAL_GROUP_DATA', + 'SAVE_FILE', + 'GET_ACCOUNT_DATA', + 'GET_ACCOUNT_NAMES', + 'SEARCH_NAMES', + 'GET_NAME_DATA', + 'GET_QDN_RESOURCE_URL', + 'LINK_TO_QDN_RESOURCE', + 'LIST_QDN_RESOURCES', + 'SEARCH_QDN_RESOURCES', + 'FETCH_QDN_RESOURCE', + 'GET_QDN_RESOURCE_STATUS', + 'GET_QDN_RESOURCE_PROPERTIES', + 'GET_QDN_RESOURCE_METADATA', + 'SEARCH_CHAT_MESSAGES', + 'LIST_GROUPS', + 'GET_BALANCE', + 'GET_AT', + 'GET_AT_DATA', + 'LIST_ATS', + 'FETCH_BLOCK', + 'FETCH_BLOCK_RANGE', + 'SEARCH_TRANSACTIONS', + 'GET_PRICE', + 'SHOW_ACTIONS' + ] + const UIQortalRequests = [ @@ -183,7 +221,7 @@ const UIQortalRequests = [ 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', - 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY', 'SIGN_TRANSACTION', 'ADMIN_ACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA' + 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY', 'SIGN_TRANSACTION', 'ADMIN_ACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'SHOW_ACTIONS' ]; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index cdc58df..8e58f59 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -250,7 +250,7 @@ export const getGroupMembers = async (groupNumber: number) => { return groupData; }; -export const decryptResource = async (data: string) => { +export const decryptResource = async (data: string, fromQortalRequest?:boolean) => { try { return new Promise((res, rej) => { window.sendMessage("decryptGroupEncryption", { @@ -261,10 +261,19 @@ export const decryptResource = async (data: string) => { res(response); return; } - rej(response.error); + if(fromQortalRequest){ + rej({error: response.error, message: response?.error}); + } else { + rej(response.error); + + } }) .catch((error) => { - rej(error.message || "An error occurred"); + if(fromQortalRequest){ + rej({message: error.message || "An error occurred", error: error.message || "An error occurred"}); + } else { + rej(error.message || "An error occurred",); + } }); }); diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 3cb5c4f..d5750b3 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -24,15 +24,12 @@ import { TextField, Typography, } from "@mui/material"; -import { - AutoSizer, - CellMeasurer, - CellMeasurerCache, - List, -} from "react-virtualized"; + import { getNameInfo } from "./Group"; import { getBaseApi, getFee } from "../../background"; import { LoadingButton } from "@mui/lab"; +import LockIcon from '@mui/icons-material/Lock'; +import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { MyContext, getArbitraryEndpointReact, @@ -49,8 +46,9 @@ import ShortUniqueId from "short-unique-id"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { getGroupNames } from "./UserListOfInvites"; import { WrapperUserAction } from "../WrapperUserAction"; -import LockIcon from '@mui/icons-material/Lock'; -import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; +import { useVirtualizer } from "@tanstack/react-virtual"; +import ErrorBoundary from "../../common/ErrorBoundary"; + export const requestQueuePromos = new RequestQueueWithPromise(20); export function utf8ToBase64(inputString: string): string { @@ -67,10 +65,7 @@ export function utf8ToBase64(inputString: string): string { const uid = new ShortUniqueId({ length: 8 }); -const cache = new CellMeasurerCache({ - fixedWidth: true, - defaultHeight: 50, -}); + export function getGroupId(str) { const match = str.match(/group-(\d+)-/); @@ -101,6 +96,18 @@ export const ListOfGroupPromotions = () => { const { show, setTxList } = useContext(MyContext); const listRef = useRef(); + const rowVirtualizer = useVirtualizer({ + count: promotions.length, + getItemKey: React.useCallback( + (index) => promotions[index]?.identifier, + [promotions] + ), + getScrollElement: () => listRef.current, + estimateSize: () => 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 + }); + + useEffect(() => { try { @@ -189,7 +196,7 @@ export const ListOfGroupPromotions = () => { }, initialDelay); return () => clearTimeout(initialTimeout); - }, [getPromotions]); + }, [getPromotions, promotionTimeInterval]); const handlePopoverOpen = (event, index) => { setPopoverAnchor(event.currentTarget); @@ -323,286 +330,6 @@ export const ListOfGroupPromotions = () => { } }; - // const handleCancelInvitation = async (address)=> { - // try { - // const fee = await getFee('CANCEL_GROUP_INVITE') - // await show({ - // message: "Would you like to perform a CANCEL_GROUP_INVITE transaction?" , - // publishFee: fee.fee + ' QORT' - // }) - // setIsLoadingCancelInvite(true) - // await new Promise((res, rej)=> { - // window.sendMessage("cancelInvitationToGroup", { - // groupId, - // qortalAddress: address, - // }) - // .then((response) => { - // if (!response?.error) { - // setInfoSnack({ - // type: "success", - // message: "Successfully canceled invitation. It may take a couple of minutes for the changes to propagate", - // }); - // setOpenSnack(true); - // handlePopoverClose(); - // setIsLoadingCancelInvite(true); - // res(response); - // return; - // } - // setInfoSnack({ - // type: "error", - // message: response?.error, - // }); - // setOpenSnack(true); - // rej(response.error); - // }) - // .catch((error) => { - // setInfoSnack({ - // type: "error", - // message: error.message || "An error occurred", - // }); - // setOpenSnack(true); - // rej(error); - // }); - - // }) - // } catch (error) { - - // } finally { - // setIsLoadingCancelInvite(false) - // } - // } - - const rowRenderer = ({ index, key, parent, style }) => { - const promotion = promotions[index]; - - return ( - - {({ measure }) => ( -
- - { - if (reason === "backdropClick") { - // Prevent closing on backdrop click - return; - } - handlePopoverClose(); // Close only on other events like Esc key press - }} - anchorOrigin={{ - vertical: "top", - horizontal: "center", - }} - transformOrigin={{ - vertical: "bottom", - horizontal: "center", - }} - style={{ marginTop: "8px" }} - > - - - Group name: {` ${promotion?.groupName}`} - - - Number of members: {` ${promotion?.memberCount}`} - - {promotion?.description && ( - - {promotion?.description} - - )} - {promotion?.isOpen === false && ( - - *This is a closed/private group, so you will need to wait - until an admin accepts your request - - )} - - - - Close - - - handleJoinGroup(promotion, promotion?.isOpen) - } - > - Join - - - - - - - - - {promotion?.name?.charAt(0)} - - - {promotion?.name} - - - - - - - {promotion?.groupName} - - - - {promotion?.isOpen === false && ( - - )} - {promotion?.isOpen === true && ( - - )} - - {promotion?.isOpen ? 'Public group' : 'Private group' } - - - - - {promotion?.data} - - - - - - - -
- )} -
- ); - }; return ( @@ -697,25 +424,293 @@ export const ListOfGroupPromotions = () => { )} -
- - {({ height, width }) => ( - +
- )} - -
+ className="scrollable-container" + style={{ + flexGrow: 1, + overflow: "auto", + position: "relative", + display: "flex", + height: "0px", + }} + > +
+
+ {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const index = virtualRow.index; + const promotion = promotions[index]; + return ( + +
+ + Error loading content: Invalid Data + + } + > + + { + if (reason === "backdropClick") { + // Prevent closing on backdrop click + return; + } + handlePopoverClose(); // Close only on other events like Esc key press + }} + anchorOrigin={{ + vertical: "top", + horizontal: "center", + }} + transformOrigin={{ + vertical: "bottom", + horizontal: "center", + }} + style={{ marginTop: "8px" }} + > + + + Group name: {` ${promotion?.groupName}`} + + + Number of members: {` ${promotion?.memberCount}`} + + {promotion?.description && ( + + {promotion?.description} + + )} + {promotion?.isOpen === false && ( + + *This is a closed/private group, so you will need to wait + until an admin accepts your request + + )} + + + + Close + + + handleJoinGroup(promotion, promotion?.isOpen) + } + > + Join + + + + + + + + + {promotion?.name?.charAt(0)} + + + {promotion?.name} + + + + + + + {promotion?.groupName} + + + + {promotion?.isOpen === false && ( + + )} + {promotion?.isOpen === true && ( + + )} + + {promotion?.isOpen ? 'Public group' : 'Private group' } + + + + + {promotion?.data} + + + + + + + + +
+ + ); + })} +
+
+
+ + @@ -726,7 +721,7 @@ export const ListOfGroupPromotions = () => { aria-describedby="alert-dialog-description" > - {"Promote your group to non-members"} + {"Promote your group to non-members"} diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index 2bf00fe..9ca3a17 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -17,9 +17,9 @@ import { InviteMember } from "./InviteMember"; import { ListOfInvites } from "./ListOfInvites"; import { ListOfBans } from "./ListOfBans"; import { ListOfJoinRequests } from "./ListOfJoinRequests"; -import { Box, Tab, Tabs } from "@mui/material"; +import { Box, Card, Tab, Tabs } from "@mui/material"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { MyContext, isMobile } from "../../App"; +import { MyContext, getBaseApiReact, isMobile } from "../../App"; import { getGroupMembers, getNames } from "./Group"; import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; import { getFee } from "../../background"; @@ -59,6 +59,8 @@ export const ManageMembers = ({ const [infoSnack, setInfoSnack] = React.useState(null); const [isLoadingMembers, setIsLoadingMembers] = React.useState(false) const [isLoadingLeave, setIsLoadingLeave] = React.useState(false) + const [groupInfo, setGroupInfo] = React.useState(null) + const handleChange = (event: React.SyntheticEvent, newValue: number) => { setValue(newValue); }; @@ -131,9 +133,20 @@ export const ManageMembers = ({ } catch (error) {} }; + const getGroupInfo = async (groupId) => { + try { + const response = await fetch( + `${getBaseApiReact()}/groups/${groupId}` + ); + const groupData = await response.json(); + setGroupInfo(groupData) + } catch (error) {} + }; + React.useEffect(()=> { if(selectedGroup?.groupId){ getMembers(selectedGroup?.groupId) + getGroupInfo(selectedGroup?.groupId) } }, [selectedGroup?.groupId]) @@ -249,12 +262,23 @@ export const ManageMembers = ({ + + + GroupId: {groupInfo?.groupId} + GroupName: {groupInfo?.groupName} + Number of members: {groupInfo?.memberCount} + + {selectedGroup?.groupId && !isOwner && ( - Leave Group )} + {value === 0 && ( { } break; } + + case "SHOW_ACTIONS" : { + try { + + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: listOfAllQortalRequests, + 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; } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 6b8fc2c..586cbfc 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -410,7 +410,7 @@ export const encryptData = async (data, sender) => { export const encryptQortalGroupData = async (data, sender) => { - let data64 = data.data64; + let data64 = data?.data64 || data?.base64; let groupId = data?.groupId let isAdmins = data?.isAdmins if(!groupId){ @@ -445,7 +445,7 @@ export const encryptQortalGroupData = async (data, sender) => { url ); const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData); + const decryptedKey: any = await decryptResource(resData, true); const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); @@ -479,7 +479,7 @@ url url ); const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData); + const decryptedKey: any = await decryptResource(resData, true); const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); @@ -509,7 +509,7 @@ url }; export const decryptQortalGroupData = async (data, sender) => { - let data64 = data.data64; + let data64 = data?.data64 || data?.base64; let groupId = data?.groupId let isAdmins = data?.isAdmins if(!groupId){ @@ -540,7 +540,7 @@ export const decryptQortalGroupData = async (data, sender) => { url ); const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData); + const decryptedKey: any = await decryptResource(resData, true); const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); @@ -571,7 +571,7 @@ url url ); const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData); + const decryptedKey: any = await decryptResource(resData, true); const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); @@ -598,7 +598,7 @@ url }; export const encryptDataWithSharingKey = async (data, sender) => { - let data64 = data.data64; + let data64 = data?.data64 || data?.base64; let publicKeys = data.publicKeys || []; if (data.fileId) { data64 = await getFileFromContentScript(data.fileId); @@ -3697,7 +3697,10 @@ export const createAndCopyEmbedLink = async (data, isFromExtension) => { }; export const getHostedData = async (data, isFromExtension) => { - + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error("This action cannot be done through a gateway"); + } const resPermission = await getUserPermission( { text1: "Do you give this application permission to", @@ -3709,18 +3712,19 @@ export const getHostedData = async (data, isFromExtension) => { if(accepted){ const limit = data?.limit ? data?.limit : 20; - const query = data?.query ? data?.query : undefined + const query = data?.query ? data?.query : "" const offset = data?.offset ? data?.offset : 0 - try { - - const url = await createEndpoint(`/arbitrary/hosted/resources/?limit=${limit}&query=${query}&offset=${offset}`); - const response = await fetch(url); - const data = await response.json(); - return data - } catch (error) { - throw error + 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"); @@ -3729,6 +3733,10 @@ export const getHostedData = async (data, isFromExtension) => { }; export const deleteHostedData = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error("This action cannot be done through a gateway"); + } const requiredFields = ["hostedData"]; const missingFields: string[] = []; requiredFields.forEach((field) => { @@ -3750,7 +3758,7 @@ export const deleteHostedData = async (data, isFromExtension) => { for (const hostedDataItem of hostedData){ try { - const url = await createEndpoint(`/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifer}`); + const url = await createEndpoint(`/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}`); await fetch(url, { method: "DELETE", headers: {