From 3c00d400933e2cfc6fc5f76d32a0a416671ea393 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 16 Apr 2025 18:58:04 +0300 Subject: [PATCH] added qrs , modified blocks, --- src/App.tsx | 49 ++- src/atoms/global.ts | 13 + src/background.ts | 144 +++++++++ src/components/Apps/AppsCategory.tsx | 4 +- src/components/Apps/AppsCategoryDesktop.tsx | 4 +- src/components/Apps/AppsLibrary.tsx | 4 +- src/components/Apps/AppsLibraryDesktop.tsx | 4 +- .../Apps/useQortalMessageListener.tsx | 12 +- src/components/Chat/useBlockUsers.tsx | 163 ++++++---- src/components/Group/BlockedUsersModal.tsx | 289 +++++++++++++---- src/components/Group/Group.tsx | 18 +- src/components/WrapperUserAction.tsx | 9 +- src/messaging/messagesToBackground.tsx | 2 +- src/qortalRequests.ts | 78 ++++- src/qortalRequests/get.ts | 295 +++++++++++++++++- src/transactions/BuyNameTransacion.ts | 45 +++ src/transactions/CancelSellNameTransacion.ts | 33 ++ src/transactions/SellNameTransacion.ts | 40 +++ src/transactions/UpdateGroupTransaction.ts | 62 ++++ src/transactions/transactions.ts | 8 + 20 files changed, 1107 insertions(+), 169 deletions(-) create mode 100644 src/transactions/BuyNameTransacion.ts create mode 100644 src/transactions/CancelSellNameTransacion.ts create mode 100644 src/transactions/SellNameTransacion.ts create mode 100644 src/transactions/UpdateGroupTransaction.ts diff --git a/src/App.tsx b/src/App.tsx index 041535c..ecc8a7b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,7 @@ import { DialogContentText, DialogTitle, Divider, + FormControlLabel, Input, InputLabel, Popover, @@ -29,6 +30,7 @@ import { } from "@mui/material"; import { decryptStoredWallet } from "./utils/decryptWallet"; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; +import PriorityHighIcon from '@mui/icons-material/PriorityHigh'; import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite'; import 'react-json-view-lite/dist/index.css'; @@ -486,6 +488,8 @@ function App() { url: "http://127.0.0.1:12391", }); const [useLocalNode, setUseLocalNode] = useState(false); + const [confirmRequestRead, setConfirmRequestRead] = useState(false); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [showSeed, setShowSeed] = useState(false) const [creationStep, setCreationStep] = useState(1) @@ -878,6 +882,8 @@ function App() { if(message?.payload?.checkbox1){ qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false } + setConfirmRequestRead(false) + await showQortalRequestExtension(message?.payload); if (qortalRequestCheckbox1Ref.current) { event.source.postMessage( @@ -3110,7 +3116,7 @@ await showInfo({ > { @@ -3341,6 +3347,35 @@ await showInfo({ )} + {messageQortalRequestExtension?.confirmCheckbox && ( + setConfirmRequestRead(e.target.checked)} + checked={confirmRequestRead} + edge="start" + tabIndex={-1} + disableRipple + sx={{ + "&.Mui-checked": { + color: "white", + }, + "& .MuiSvgIcon-root": { + color: "white", + }, + }} + /> + } + label={ + + + I have read this request + + + + } + /> + )} - { + if(messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead) return + onOkQortalRequestExtension("accepted") }} - onClick={() => onOkQortalRequestExtension("accepted")} > accept diff --git a/src/atoms/global.ts b/src/atoms/global.ts index e98f9e6..3b4028c 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -41,6 +41,14 @@ export const sortablePinnedAppsAtom = atom({ { name: 'Q-Wallets', service: 'APP' + }, + { + name: 'Q-Search', + service: 'APP' + }, + { + name: 'Q-Nodecontrol', + service: 'APP' } ], }); @@ -180,3 +188,8 @@ export const lastPaymentSeenTimestampAtom = atom({ key: 'lastPaymentSeenTimestampAtom', default: null, }); + +export const isOpenBlockedModalAtom = atom({ + key: 'isOpenBlockedModalAtom', + default: false, +}); \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index 5534aa1..8fbada2 100644 --- a/src/background.ts +++ b/src/background.ts @@ -2247,6 +2247,150 @@ export async function createGroup({ if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed"); return res; } + +export async function sellName({ + name, + sellPrice +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("SELL_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(5, keyPair, { + fee: feeres.fee, + name, + sellPrice: sellPrice, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function cancelSellName({ + name +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("SELL_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(6, keyPair, { + fee: feeres.fee, + name, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function buyName({ + name, + sellerAddress, + sellPrice +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("BUY_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(7, keyPair, { + fee: feeres.fee, + name, + sellPrice, + recipient: sellerAddress, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function updateGroup({ + groupId, + newOwner, + newIsOpen, + newDescription, + newApprovalThreshold, + newMinimumBlockDelay, + newMaximumBlockDelay +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("UPDATE_GROUP"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(23, keyPair, { + fee: feeres.fee, + _groupId: groupId, + newOwner, + newIsOpen, + newDescription, + newApprovalThreshold, + newMinimumBlockDelay, + newMaximumBlockDelay, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + + export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) { const address = await getNameOrAddress(qortalAddress); if (!address) throw new Error("Cannot find user"); diff --git a/src/components/Apps/AppsCategory.tsx b/src/components/Apps/AppsCategory.tsx index be2fa1a..7804333 100644 --- a/src/components/Apps/AppsCategory.tsx +++ b/src/components/Apps/AppsCategory.tsx @@ -41,7 +41,9 @@ const officialAppList = [ "q-trade", "q-support", "q-manager", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 3844907..4958bab 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -47,7 +47,9 @@ const officialAppList = [ "q-fund", "q-shop", "q-manager", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 1650e28..ebd93d8 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -45,7 +45,9 @@ const officialAppList = [ "q-support", "q-manager", "q-mintership", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index d2255e1..30cb0cc 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -56,7 +56,9 @@ const officialAppList = [ "q-shop", "q-manager", "q-mintership", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index f291c9f..5d975b5 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -255,7 +255,11 @@ export function openIndexedDB() { 'GET_NODE_INFO', 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', - 'SHOW_PDF_READER' + 'SHOW_PDF_READER', + 'UPDATE_GROUP', + 'SELL_NAME', + 'CANCEL_SELL_NAME', + 'BUY_NAME' ] @@ -269,7 +273,9 @@ const UIQortalRequests = [ '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_PUBLIC_NODE', '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', 'REGISTER_NAME', 'UPDATE_NAME', 'LEAVE_GROUP', 'INVITE_TO_GROUP', 'KICK_FROM_GROUP', 'BAN_FROM_GROUP', 'CANCEL_GROUP_BAN', 'ADD_GROUP_ADMIN', 'REMOVE_GROUP_ADMIN','DECRYPT_AESGCM', 'CANCEL_GROUP_INVITE', 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS', 'GET_NODE_INFO', - 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'SHOW_PDF_READER' + 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'SHOW_PDF_READER', 'UPDATE_GROUP', 'SELL_NAME', + 'CANCEL_SELL_NAME', + 'BUY_NAME' ]; @@ -551,7 +557,7 @@ isDOMContentLoaded: false result: null, error: { error: response.error, - message: typeof response?.error === 'string' ? response.error : 'An error has occurred' + message: typeof response?.error === 'string' ? response?.error : typeof response?.message === 'string' ? response?.message : 'An error has occurred' }, }); } else { diff --git a/src/components/Chat/useBlockUsers.tsx b/src/components/Chat/useBlockUsers.tsx index 05cbe90..42daeb4 100644 --- a/src/components/Chat/useBlockUsers.tsx +++ b/src/components/Chat/useBlockUsers.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useRef } from "react"; -import { getBaseApiReact } from "../../App"; -import { truncate } from "lodash"; + @@ -19,7 +18,7 @@ export const useBlockedAddresses = () => { const isUserBlocked = useCallback((address, name)=> { try { if(!address) return false - if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true + if(userBlockedRef.current[address]) return true return false @@ -90,43 +89,13 @@ export const useBlockedAddresses = () => { }, []) const removeBlockFromList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - window.sendMessage("listActions", { - - type: 'remove', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(!name){ - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject - } else { - const copyObject = {...userNamesBlockedRef.current} - delete copyObject[name] - userNamesBlockedRef.current = copyObject - } - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) - if(name && userBlockedRef.current[address]){ + if(name){ await new Promise((res, rej) => { window.sendMessage("listActions", { type: 'remove', - items: !name ? [name] : [address], - listName: !name ? 'blockedNames' : 'blockedAddresses' + items: [name] , + listName: 'blockedNames' }) .then((response) => { @@ -134,9 +103,12 @@ export const useBlockedAddresses = () => { rej(response?.message); return; } else { - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject + + const copyObject = {...userNamesBlockedRef.current} + delete copyObject[name] + userNamesBlockedRef.current = copyObject + + res(response); } }) @@ -145,42 +117,95 @@ export const useBlockedAddresses = () => { }); }) } + + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'remove', + items: [address], + listName: 'blockedAddresses' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + + const copyObject = {...userBlockedRef.current} + delete copyObject[address] + userBlockedRef.current = copyObject + + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); + }) + } + }, []) const addToBlockList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - window.sendMessage("listActions", { - - type: 'add', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(name){ - - const copyObject = {...userNamesBlockedRef.current} - copyObject[name] = true - userNamesBlockedRef.current = copyObject - }else { - const copyObject = {...userBlockedRef.current} - copyObject[address] = true - userBlockedRef.current = copyObject - - } + if(name){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { - res(response); - } + type: 'add', + items: [name], + listName: 'blockedNames' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = {...userNamesBlockedRef.current} + copyObject[name] = true + userNamesBlockedRef.current = copyObject + + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + } + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'add', + items: [address], + listName: 'blockedAddresses' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + + const copyObject = {...userBlockedRef.current} + copyObject[address] = true + userBlockedRef.current = copyObject + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); + }) + } + }, []) return { diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 84fa3fa..f82d632 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -10,15 +10,23 @@ import { Typography, } from "@mui/material"; import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import { getBaseApiReact, MyContext } from "../../App"; import { Spacer } from "../../common/Spacer"; -import { executeEvent } from "../../utils/events"; - -export const BlockedUsersModal = ({ close }) => { +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 [hasChanged, setHasChanged] = useState(false); const [value, setValue] = useState(""); - - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext); + const [addressesWithNames, setAddressesWithNames] = useState({}) + const { isShow, onCancel, onOk, show, message } = useModal(); + const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } = + useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, names: {}, @@ -28,60 +36,162 @@ export const BlockedUsersModal = ({ close }) => { }; useEffect(() => { + if(!isOpenBlockedModal) return fetchBlockedUsers(); - }, []); + }, [isOpenBlockedModal]); + + const getNames = async () => { + // const validApi = await findUsableApi(); + 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 + } + + + return true; + }); + + await Promise.all(getMemNames); + + setAddressesWithNames(addressNames) + }; + + const blockUser = async (e, user?: string) => { + try { + const valUser = user || value + if (!valUser) return; + const isAddress = validateAddress(valUser); + let userName = null; + let userAddress = null; + if (isAddress) { + userAddress = valUser; + const name = await getNameInfo(valUser); + if (name) { + userName = name; + } + } + 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) { + userAddress = data.owner; + userName = valUser; + } + } + if(!userName){ + await addToBlockList(userAddress, null); + fetchBlockedUsers(); + setHasChanged(true); + executeEvent('updateChatMessagesWithBlocks', true) + setValue('') + return + } + const responseModal = await show({ + userName, + userAddress, + }); + if (responseModal === "both") { + await addToBlockList(userAddress, userName); + } else if (responseModal === "address") { + await addToBlockList(userAddress, null); + } else if (responseModal === "name") { + await addToBlockList(null, userName); + } + fetchBlockedUsers(); + setHasChanged(true); + setValue('') + if(user){ + setIsOpenBlockedModal(false) + } + if(responseModal === 'both' || responseModal === 'address'){ + executeEvent('updateChatMessagesWithBlocks', true) + } + } catch (error) { + setOpenSnackGlobal(true); + + setInfoSnackCustom({ + type: "error", + message: error?.message || "Unable to block user", + }); + } + }; + const blockUserFromOutsideModalFunc = (e) => { + const user = e.detail?.user; + setIsOpenBlockedModal(true) + blockUser(null, user) + }; + + useEffect(() => { + subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + + return () => { + unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + }; + }, []); return ( - Blocked Users - - Blocked Users + - { - setValue(e.target.value); + > + - - - + > + { + setValue(e.target.value); + }} + /> + + + {Object.entries(blockedUsers?.addresses).length > 0 && ( <> - Blocked Users for Chat ( addresses ) + Blocked addresses- blocks processing of txs + + )} - + {Object.entries(blockedUsers?.addresses || {})?.map( ([key, value]) => { return ( @@ -90,18 +200,22 @@ export const BlockedUsersModal = ({ close }) => { display: "flex", alignItems: "center", gap: "10px", - width: '100%', - justifyContent: 'space-between' + width: "100%", + justifyContent: "space-between", }} > - {key} + {addressesWithNames[key] || key} + + + + {"Decide what to block"} + + + + Blocking {message?.userName || message?.userAddress} + + + Choose "block txs" or "all" to block chat messages + + + + + + + + ); }; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 4e61695..9ba26c7 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -19,7 +19,8 @@ import React, { useRef, useState, } from "react"; -import BlockIcon from '@mui/icons-material/Block'; +import PersonOffIcon from '@mui/icons-material/PersonOff'; + import { WalletsAppWrapper } from "./WalletsAppWrapper"; import SettingsIcon from "@mui/icons-material/Settings"; @@ -99,7 +100,7 @@ import { formatEmailDate } from "./QMailMessages"; import { useHandleMobileNativeBack } from "../../hooks/useHandleMobileNativeBack"; import { AdminSpace } from "../Chat/AdminSpace"; import { useRecoilState, useSetRecoilState } from "recoil"; -import { addressInfoControllerAtom, groupsPropertiesAtom, lastEnteredGroupIdAtom, selectedGroupIdAtom } from "../../atoms/global"; +import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, lastEnteredGroupIdAtom, selectedGroupIdAtom } from "../../atoms/global"; import { sortArrayByTimestampAndGroupName } from "../../utils/time"; import { BlockedUsersModal } from "./BlockedUsersModal"; import { GlobalTouchMenu } from "../GlobalTouchMenu"; @@ -483,6 +484,8 @@ export const Group = ({ const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); + const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom) + const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); const [drawerMode, setDrawerMode] = React.useState("groups"); @@ -507,7 +510,6 @@ export const Group = ({ const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false) const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom) const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom); - const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false); const setLastEnteredGroupIdAtom = useSetRecoilState(lastEnteredGroupIdAtom) const isPrivate = useMemo(()=> { if(selectedGroup?.groupId === '0') return false @@ -2159,7 +2161,7 @@ export const Group = ({ padding: '10px' }} > - )} - {isOpenBlockedUserModal && ( - { - setIsOpenBlockedUserModal(false) - }} /> - )} + + + {selectedDirect && !newChat && ( <> { onClick={async () => { try { setIsLoading(true) - if(isAlreadyBlocked === true){ - await removeBlockFromList(address, name) - } else if(isAlreadyBlocked === false) { - await addToBlockList(address, name) - } - executeEvent('updateChatMessagesWithBlocks', true) + executeEvent("blockUserFromOutside", { + user: address + }) } catch (error) { console.error(error) } finally { diff --git a/src/messaging/messagesToBackground.tsx b/src/messaging/messagesToBackground.tsx index 1304b41..ede46be 100644 --- a/src/messaging/messagesToBackground.tsx +++ b/src/messaging/messagesToBackground.tsx @@ -24,7 +24,7 @@ window.addEventListener("message", (event) => { } }); -export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo, skipAuth) => { +export const sendMessageBackground = (action, data = {}, timeout = 240000, isExtension, appInfo, skipAuth) => { return new Promise((resolve, reject) => { const requestId = generateRequestId(); // Unique ID for each request callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index d4f765a..d8ef5e5 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,6 +1,6 @@ import { gateways, getApiKeyFromStorage } from "./background"; import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener"; -import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, 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 } from "./qortalRequests/get"; +import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, buyNameRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellNameRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sellNameRequest, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateGroupRequest, updateNameRequest, voteOnPoll } from "./qortalRequests/get"; import { getData, storeData } from "./utils/chromeStorage"; import { executeEvent } from "./utils/events"; @@ -1206,6 +1206,82 @@ export const isRunningGateway = async ()=> { } 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({ + requestId: request.requestId, + action: request.action, + error: error?.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } + 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; } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 016b7cf..430b262 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -27,7 +27,12 @@ import { makeAdmin, removeAdmin, cancelInvitationToGroup, - createGroup + createGroup, + updateGroup, + getBaseApi, + buyName, + cancelSellName, + sellName } from "../background"; import { getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption"; import { showSaveFilePicker } from "../components/Apps/useQortalMessageListener"; @@ -373,7 +378,7 @@ async function getUserPermission(payload, isFromExtension) { responseResolvers.get(requestId)(false); // Resolve with `false` if no response responseResolvers.delete(requestId); } - }, 30000); // 30-second timeout + }, 60000); // 30-second timeout }); } @@ -1229,6 +1234,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1237,6 +1243,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1267,6 +1274,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1294,6 +1302,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1320,7 +1329,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten apiVersion: 2, withFee: true, }, - ], false); + ], true); await new Promise((res) => { setTimeout(() => { res(); @@ -1331,21 +1340,25 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); } } catch (error) { failedPublishesIdentifiers.push({ reason: error?.message || "Unknown error", identifier: resource.identifier, + service: resource.service, }); } } if (failedPublishesIdentifiers.length > 0) { - const obj = {}; - obj["error"] = { - unsuccessfulPublishes: failedPublishesIdentifiers, - }; - return obj; + const obj = { + message: "Some resources have failed to publish.", + }; + obj["error"] = { + unsuccessfulPublishes: failedPublishesIdentifiers, + }; + return obj; } if(hasAppFee && checkbox1){ sendCoinFunc({ @@ -2734,7 +2747,8 @@ export const sendCoin = async (data, isFromExtension) => { text1: "Do you give this application permission to send coins?", text2: `To: ${recipient}`, highlightedText: `${amount} ${checkCoin}`, - fee: fee + fee: fee, + confirmCheckbox: true }, isFromExtension); const { accepted } = resPermission; @@ -3829,6 +3843,11 @@ export const registerNameRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const fee = await getFee("REGISTER_NAME"); const resPermission = await getUserPermission( { @@ -3842,7 +3861,7 @@ export const registerNameRequest = async (data, isFromExtension) => { const { accepted } = resPermission; if (accepted) { const name = data.name - const description = data?.description + const description = data?.description || "" const response = await registerName({ name, description }); return response @@ -3859,9 +3878,14 @@ export const updateNameRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const oldName = data.oldName const newName = data.newName - const description = data?.description + const description = data?.description || "" const fee = await getFee("UPDATE_NAME"); const resPermission = await getUserPermission( { @@ -3890,6 +3914,11 @@ export const leaveGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId let groupInfo = null; try { @@ -3930,6 +3959,11 @@ export const inviteToGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.inviteeAddress const inviteTime = data?.inviteTime @@ -3979,6 +4013,11 @@ export const kickFromGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress const reason = data?.reason @@ -4028,6 +4067,11 @@ export const banFromGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress const rBanTime = data?.banTime @@ -4078,6 +4122,11 @@ export const cancelGroupBanRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4125,6 +4174,11 @@ export const addGroupAdminRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4172,6 +4226,11 @@ export const removeGroupAdminRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4219,6 +4278,11 @@ export const cancelGroupInviteRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4313,15 +4377,20 @@ export const decryptAESGCMRequest = async (data, isFromExtension) => { }; export const createGroupRequest = async (data, isFromExtension) => { - const requiredFields = ["groupId", "qortalAddress"]; + const requiredFields = ["groupId", "qortalAddress", "groupName", "type", "approvalThreshold", "minBlock", "maxBlock"]; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (!data[field]) { + if (data[field] !== undefined && data[field] !== null) { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupName = data.groupName - const description = data?.description + const description = data?.description || "" const type = +data.type const approvalThreshold = +data?.approvalThreshold const minBlock = +data?.minBlock @@ -4354,6 +4423,70 @@ export const createGroupRequest = async (data, isFromExtension) => { } }; +export const updateGroupRequest = async (data, isFromExtension) => { + const requiredFields = ["groupId", "newOwner", "type", "approvalThreshold", "minBlock", "maxBlock"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const groupId = +data.groupId + const newOwner = data.newOwner + const description = data?.description || "" + const type = +data.type + const approvalThreshold = +data?.approvalThreshold + const minBlock = +data?.minBlock + const maxBlock = +data.maxBlock + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) throw new Error("Failed to fetch group"); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = (error && error.message) || "Group not found"; + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(newOwner) + + + const fee = await getFee("CREATE_GROUP"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to update this group?`, + text2: `New owner: ${displayInvitee || newOwner}`, + highlightedText: `Group: ${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateGroup({ + groupId, + newOwner, + newIsOpen: type, + newDescription: description, + newApprovalThreshold: approvalThreshold, + newMinimumBlockDelay: minBlock, + newMaximumBlockDelay: maxBlock + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + export const getUserWalletTransactions = async (data, isFromExtension, appInfo) => { const requiredFields = ["coin"]; const missingFields: string[] = []; @@ -4362,6 +4495,7 @@ export const getUserWalletTransactions = async (data, isFromExtension, appInfo) missingFields.push(field); } }); + if (missingFields.length > 0) { const missingFieldsString = missingFields.join(", "); const errorMsg = `Missing fields: ${missingFieldsString}`; @@ -4537,4 +4671,137 @@ export const getArrrSyncStatus = async () => { } catch (error) { throw new Error(error?.message || "Error in retrieving arrr sync status"); } +}; + +export const sellNameRequest = async (data, isFromExtension) => { + const requiredFields = ["salePrice", "nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + const sellPrice = +data.salePrice + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); +if(!nameData) throw new Error("This name does not exist") + +if(nameData?.isForSale) throw new Error("This name is already for sale") + const fee = await getFee("SELL_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to create a sell name transaction?`, + highlightedText: `Sell ${name} for ${sellPrice} QORT`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await sellName({ + name, + sellPrice + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + +export const cancelSellNameRequest = async (data, isFromExtension) => { + const requiredFields = ["nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); +if(!nameData?.isForSale) throw new Error("This name is not for sale") + + const fee = await getFee("CANCEL_SELL_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to cancel the selling of a name?`, + highlightedText: `Name: ${name}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelSellName({ + name + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + +export const buyNameRequest = async (data, isFromExtension) => { + const requiredFields = ["nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); + if(!nameData?.isForSale) throw new Error("This name is not for sale") + const sellerAddress = nameData.owner + const sellPrice = +nameData.salePrice + + + const fee = await getFee("BUY_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to buy a name?`, + highlightedText: `Buying ${name} for ${sellPrice} QORT`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await buyName({ + name, + sellerAddress, + sellPrice + }) + return response + + } else { + throw new Error("User declined request"); + } }; \ No newline at end of file diff --git a/src/transactions/BuyNameTransacion.ts b/src/transactions/BuyNameTransacion.ts new file mode 100644 index 0000000..8c321d3 --- /dev/null +++ b/src/transactions/BuyNameTransacion.ts @@ -0,0 +1,45 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class BuyNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 7 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + set sellPrice(sellPrice) { + this._sellPrice = sellPrice * QORT_DECIMALS + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) + } + + set recipient(recipient) { + this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) + this.theRecipient = recipient + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._recipient, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/CancelSellNameTransacion.ts b/src/transactions/CancelSellNameTransacion.ts new file mode 100644 index 0000000..04296c8 --- /dev/null +++ b/src/transactions/CancelSellNameTransacion.ts @@ -0,0 +1,33 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class CancelSellNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 6 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/SellNameTransacion.ts b/src/transactions/SellNameTransacion.ts new file mode 100644 index 0000000..3926568 --- /dev/null +++ b/src/transactions/SellNameTransacion.ts @@ -0,0 +1,40 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class SellNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 5 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + set sellPrice(sellPrice) { + this.showSellPrice = sellPrice + this._sellPrice = sellPrice * QORT_DECIMALS + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/UpdateGroupTransaction.ts b/src/transactions/UpdateGroupTransaction.ts new file mode 100644 index 0000000..9d9856b --- /dev/null +++ b/src/transactions/UpdateGroupTransaction.ts @@ -0,0 +1,62 @@ +// @ts-nocheck + + +import { QORT_DECIMALS } from "../constants/constants"; +import TransactionBase from "./TransactionBase"; + +export default class UpdateGroupTransaction extends TransactionBase { + constructor() { + super() + this.type = 23 + } + + + + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + set newOwner(newOwner) { + this._newOwner = newOwner instanceof Uint8Array ? newOwner : this.constructor.Base58.decode(newOwner) + } + set newIsOpen(newIsOpen) { + + this._rGroupType = new Uint8Array(1) + this._rGroupType[0] = newIsOpen + } + set newDescription(newDescription) { + this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(newDescription.toLocaleLowerCase()) + this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length) + } + set newApprovalThreshold(newApprovalThreshold) { + this._rGroupApprovalThreshold = new Uint8Array(1) + this._rGroupApprovalThreshold[0] = newApprovalThreshold; + } + set newMinimumBlockDelay(newMinimumBlockDelay) { + this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMinimumBlockDelay) + } + set newMaximumBlockDelay(newMaximumBlockDelay) { + + this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMaximumBlockDelay) + } + + set _groupId(_groupId){ + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) + } + get params() { + const params = super.params + params.push( + this._groupBytes, + this._newOwner, + this._rGroupDescLength, + this._rGroupDescBytes, + this._rGroupType, + this._rGroupApprovalThreshold, + this._rGroupMinimumBlockDelayBytes, + this._rGroupMaximumBlockDelayBytes, + this._feeBytes + ) + return params + } +} \ No newline at end of file diff --git a/src/transactions/transactions.ts b/src/transactions/transactions.ts index db516a1..cb0f9c3 100644 --- a/src/transactions/transactions.ts +++ b/src/transactions/transactions.ts @@ -20,17 +20,25 @@ import DeployAtTransaction from './DeployAtTransaction.js' import RewardShareTransaction from './RewardShareTransaction.js' import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js' import UpdateNameTransaction from './UpdateNameTransaction.js' +import UpdateGroupTransaction from './UpdateGroupTransaction.js' +import SellNameTransacion from './SellNameTransacion.js' +import CancelSellNameTransacion from './CancelSellNameTransacion.js' +import BuyNameTransacion from './BuyNameTransacion.js' export const transactionTypes = { 3: RegisterNameTransaction, 4: UpdateNameTransaction, 2: PaymentTransaction, + 5: SellNameTransacion, + 6: CancelSellNameTransacion, + 7: BuyNameTransacion, 8: CreatePollTransaction, 9: VoteOnPollTransaction, 16: DeployAtTransaction, 18: ChatTransaction, 181: GroupChatTransaction, 22: CreateGroupTransaction, + 23: UpdateGroupTransaction, 24: AddGroupAdminTransaction, 25: RemoveGroupAdminTransaction, 26: GroupBanTransaction,