added qrs , modified blocks,

This commit is contained in:
PhilReact 2025-04-16 19:03:35 +03:00
parent 5b78bc4fb2
commit 0e74c396f4
17 changed files with 1090 additions and 146 deletions

View File

@ -21,6 +21,7 @@ import {
DialogContent, DialogContent,
DialogContentText, DialogContentText,
DialogTitle, DialogTitle,
FormControlLabel,
Input, Input,
InputLabel, InputLabel,
Popover, Popover,
@ -139,6 +140,8 @@ import { useBlockedAddresses } from "./components/Chat/useBlockUsers";
import { QortPayment } from "./components/QortPayment"; import { QortPayment } from "./components/QortPayment";
import { GeneralNotifications } from "./components/GeneralNotifications"; import { GeneralNotifications } from "./components/GeneralNotifications";
import { PdfViewer } from "./common/PdfViewer"; import { PdfViewer } from "./common/PdfViewer";
import PriorityHighIcon from '@mui/icons-material/PriorityHigh';
type extStates = type extStates =
| "not-authenticated" | "not-authenticated"
@ -354,6 +357,8 @@ function App() {
url: "http://127.0.0.1:12391", url: "http://127.0.0.1:12391",
}); });
const [useLocalNode, setUseLocalNode] = useState(false); const [useLocalNode, setUseLocalNode] = useState(false);
const [confirmRequestRead, setConfirmRequestRead] = useState(false);
const {downloadResource} = useFetchResources() const {downloadResource} = useFetchResources()
const [showSeed, setShowSeed] = useState(false) const [showSeed, setShowSeed] = useState(false)
const [creationStep, setCreationStep] = useState(1) const [creationStep, setCreationStep] = useState(1)
@ -716,6 +721,8 @@ function App() {
if(message?.payload?.checkbox1){ if(message?.payload?.checkbox1){
qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false
} }
setConfirmRequestRead(false)
await showQortalRequestExtension(message?.payload); await showQortalRequestExtension(message?.payload);
if (qortalRequestCheckbox1Ref.current) { if (qortalRequestCheckbox1Ref.current) {
@ -2157,11 +2164,13 @@ function App() {
justifyContent: "flex-start", justifyContent: "flex-start",
paddingLeft: "22px", paddingLeft: "22px",
boxSizing: "border-box", boxSizing: "border-box",
maxWidth: '700px'
}} }}
> >
<img <img
style={{ style={{
cursor: "pointer", cursor: "pointer",
height: '24px'
}} }}
onClick={returnToMain} onClick={returnToMain}
src={Return} src={Return}
@ -2661,11 +2670,13 @@ function App() {
justifyContent: "flex-start", justifyContent: "flex-start",
paddingLeft: "22px", paddingLeft: "22px",
boxSizing: "border-box", boxSizing: "border-box",
maxWidth: '700px'
}} }}
> >
<img <img
style={{ style={{
cursor: "pointer", cursor: "pointer",
height: '24px'
}} }}
onClick={() => { onClick={() => {
setRawWallet(null); setRawWallet(null);
@ -2689,11 +2700,13 @@ function App() {
justifyContent: "flex-start", justifyContent: "flex-start",
paddingLeft: "22px", paddingLeft: "22px",
boxSizing: "border-box", boxSizing: "border-box",
maxWidth: '700px'
}} }}
> >
<img <img
style={{ style={{
cursor: "pointer", cursor: "pointer",
height: '24px'
}} }}
onClick={() => { onClick={() => {
setRawWallet(null); setRawWallet(null);
@ -2793,11 +2806,13 @@ function App() {
justifyContent: "flex-start", justifyContent: "flex-start",
paddingLeft: "22px", paddingLeft: "22px",
boxSizing: "border-box", boxSizing: "border-box",
maxWidth: '700px'
}} }}
> >
<img <img
style={{ style={{
cursor: "pointer", cursor: "pointer",
height: '24px'
}} }}
onClick={returnToMain} onClick={returnToMain}
src={Return} src={Return}
@ -2875,11 +2890,13 @@ function App() {
justifyContent: "flex-start", justifyContent: "flex-start",
paddingLeft: "22px", paddingLeft: "22px",
boxSizing: "border-box", boxSizing: "border-box",
maxWidth: '700px'
}} }}
> >
<img <img
style={{ style={{
cursor: "pointer", cursor: "pointer",
height: '24px'
}} }}
onClick={() => { onClick={() => {
if(creationStep === 2){ if(creationStep === 2){
@ -3533,6 +3550,35 @@ function App() {
</Typography> </Typography>
</Box> </Box>
)} )}
{messageQortalRequestExtension?.confirmCheckbox && (
<FormControlLabel
control={
<Checkbox
onChange={(e) => setConfirmRequestRead(e.target.checked)}
checked={confirmRequestRead}
edge="start"
tabIndex={-1}
disableRipple
sx={{
"&.Mui-checked": {
color: "white",
},
"& .MuiSvgIcon-root": {
color: "white",
},
}}
/>
}
label={
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography sx={{ fontSize: "14px" }}>
I have read this request
</Typography>
<PriorityHighIcon color="warning" />
</Box>
}
/>
)}
<Spacer height="29px" /> <Spacer height="29px" />
<Box <Box
@ -3547,8 +3593,16 @@ function App() {
bgColor="var(--green)" bgColor="var(--green)"
sx={{ sx={{
minWidth: "102px", minWidth: "102px",
opacity: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 0.1 : 0.7,
cursor: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 'default' : 'pointer',
"&:hover": {
opacity: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 0.1 : 1,
}
}}
onClick={() => {
if(messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead) return
onOkQortalRequestExtension("accepted")
}} }}
onClick={() => onOkQortalRequestExtension("accepted")}
> >
accept accept
</CustomButtonAccept> </CustomButtonAccept>

View File

@ -37,6 +37,14 @@ export const sortablePinnedAppsAtom = atom({
{ {
name: 'Q-Wallets', name: 'Q-Wallets',
service: 'APP' service: 'APP'
},
{
name: 'Q-Search',
service: 'APP'
},
{
name: 'Q-Nodecontrol',
service: 'APP'
} }
], ],
}); });
@ -164,3 +172,7 @@ export const lastPaymentSeenTimestampAtom = atom<null | number>({
key: 'lastPaymentSeenTimestampAtom', key: 'lastPaymentSeenTimestampAtom',
default: null, default: null,
}); });
export const isOpenBlockedModalAtom = atom({
key: 'isOpenBlockedModalAtom',
default: false,
});

View File

@ -2673,6 +2673,150 @@ export async function createGroup({
if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed"); if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed");
return res; 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 }) { export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) {
const address = await getNameOrAddress(qortalAddress); const address = await getNameOrAddress(qortalAddress);
if (!address) throw new Error("Cannot find user"); if (!address) throw new Error("Cannot find user");

View File

@ -43,7 +43,9 @@ const officialAppList = [
"q-shop", "q-shop",
"q-trade", "q-trade",
"q-support", "q-support",
"q-wallets" "q-wallets",
"q-search",
"q-nodecontrol"
]; ];
const ScrollerStyled = styled('div')({ const ScrollerStyled = styled('div')({

View File

@ -60,7 +60,9 @@ const officialAppList = [
"q-support", "q-support",
"q-mintership", "q-mintership",
"q-manager", "q-manager",
"q-wallets" "q-wallets",
"q-search",
"q-nodecontrol"
]; ];
const ScrollerStyled = styled("div")({ const ScrollerStyled = styled("div")({

View File

@ -247,7 +247,10 @@ const UIQortalRequests = [
'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER',
'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', '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', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'DECRYPT_QORTAL_GROUP_DATA', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', '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', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'DECRYPT_QORTAL_GROUP_DATA', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', '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', 'UPDATE_GROUP',
'SELL_NAME',
'CANCEL_SELL_NAME',
'BUY_NAME'
]; ];
@ -614,7 +617,7 @@ isDOMContentLoaded: false
); );
} else if(event?.data?.action === 'OPEN_NEW_TAB'){ } else if(event?.data?.action === 'OPEN_NEW_TAB'){
try { try {
await openNewTab(event?.data?.payload) await openNewTab(event?.data)
event.ports[0].postMessage({ event.ports[0].postMessage({
result: true, result: true,
error: null, error: null,
@ -639,6 +642,27 @@ isDOMContentLoaded: false
error: error?.message, error: error?.message,
}); });
} }
} else if(event?.data?.action === 'SHOW_PDF_READER'){
try {
if(!event?.data?.blob){
throw new Error('Missing blob')
}
if(event?.data?.blob?.type !== "application/pdf") throw new Error('blob type must be application/pdf')
executeEvent("openPdf", { blob: event?.data?.blob});
event.ports[0].postMessage({
result: true,
error: null,
});
} catch (error) {
event.ports[0].postMessage({
result: null,
error: error?.message,
});
}
} }
}; };

View File

@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useRef } from "react"; 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)=> { const isUserBlocked = useCallback((address, name)=> {
try { try {
if(!address) return false if(!address) return false
if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true if(userBlockedRef.current[address]) return true
return false return false
@ -88,47 +87,47 @@ export const useBlockedAddresses = () => {
} }
fetchBlockedList() fetchBlockedList()
}, []) }, [])
const removeBlockFromList = useCallback(async (address, name)=> { const removeBlockFromList = useCallback(async (address, name)=> {
if(name){
await new Promise((res, rej) => { await new Promise((res, rej) => {
chrome?.runtime?.sendMessage( chrome?.runtime?.sendMessage(
{ {
action: "listActions", action: "listActions",
payload: { payload: {
type: 'remove', type: 'remove',
items: name ? [name] : [address], items: [name] ,
listName: name ? 'blockedNames' : 'blockedAddresses' listName: 'blockedNames'
}, },
}, },
(response) => { (response) => {
if (response.error) { if (response.error) {
rej(response?.message); rej(response?.message);
return; return;
} else {
if(!name){
const copyObject = {...userBlockedRef.current}
delete copyObject[address]
userBlockedRef.current = copyObject
} else { } else {
const copyObject = {...userNamesBlockedRef.current} const copyObject = {...userNamesBlockedRef.current}
delete copyObject[name] delete copyObject[name]
userNamesBlockedRef.current = copyObject userNamesBlockedRef.current = copyObject
}
res(response); res(response);
} }
} }
); );
}) })
if(name && userBlockedRef.current[address]){
}
if(address){
await new Promise((res, rej) => { await new Promise((res, rej) => {
chrome?.runtime?.sendMessage( chrome?.runtime?.sendMessage(
{ {
action: "listActions", action: "listActions",
payload: { payload: {
type: 'remove', type: 'remove',
items: !name ? [name] : [address], items: [address],
listName: !name ? 'blockedNames' : 'blockedAddresses' listName: 'blockedAddresses'
}, },
}, },
(response) => { (response) => {
@ -139,6 +138,8 @@ export const useBlockedAddresses = () => {
const copyObject = {...userBlockedRef.current} const copyObject = {...userBlockedRef.current}
delete copyObject[address] delete copyObject[address]
userBlockedRef.current = copyObject userBlockedRef.current = copyObject
res(response); res(response);
} }
} }
@ -146,40 +147,68 @@ export const useBlockedAddresses = () => {
}) })
} }
}, []) }, [])
const addToBlockList = useCallback(async (address, name)=> { const addToBlockList = useCallback(async (address, name)=> {
if(name){
await new Promise((res, rej) => {
chrome?.runtime?.sendMessage(
{
action: "listActions",
payload: {
type: 'add',
items: [name],
listName: 'blockedNames'
},
},
(response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = {...userNamesBlockedRef.current}
copyObject[name] = true
userNamesBlockedRef.current = copyObject
res(response);
}
}
);
})
}
if(address){
await new Promise((res, rej) => { await new Promise((res, rej) => {
chrome?.runtime?.sendMessage( chrome?.runtime?.sendMessage(
{ {
action: "listActions", action: "listActions",
payload: { payload: {
type: 'add', type: 'add',
items: name ? [name] : [address], items: [address],
listName: name ? 'blockedNames' : 'blockedAddresses' listName: 'blockedAddresses'
}, },
}, },
(response) => { (response) => {
if (response.error) { if (response.error) {
rej(response?.message); rej(response?.message);
return; return;
} else {
if(name){
const copyObject = {...userNamesBlockedRef.current}
copyObject[name] = true
userNamesBlockedRef.current = copyObject
} else { } else {
const copyObject = {...userBlockedRef.current} const copyObject = {...userBlockedRef.current}
copyObject[address] = true copyObject[address] = true
userBlockedRef.current = copyObject userBlockedRef.current = copyObject
}
res(response); res(response);
} }
} }
) );
}) })
}
}, []) }, [])
return { return {

View File

@ -10,15 +10,23 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { MyContext } from "../../App"; import { getBaseApiReact, MyContext } from "../../App";
import { Spacer } from "../../common/Spacer"; import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events"; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
import { validateAddress } from "../../utils/validateAddress";
export const BlockedUsersModal = ({ close }) => { 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 [hasChanged, setHasChanged] = useState(false);
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [addressesWithNames, setAddressesWithNames] = useState({})
const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext); const { isShow, onCancel, onOk, show, message } = useModal();
const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } =
useContext(MyContext);
const [blockedUsers, setBlockedUsers] = useState({ const [blockedUsers, setBlockedUsers] = useState({
addresses: {}, addresses: {},
names: {}, names: {},
@ -28,20 +36,119 @@ export const BlockedUsersModal = ({ close }) => {
}; };
useEffect(() => { useEffect(() => {
if(!isOpenBlockedModal) return
fetchBlockedUsers(); 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 ( return (
<Dialog <Dialog
open={true} open={isOpenBlockedModal}
aria-labelledby="alert-dialog-title" aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
> >
<DialogTitle>Blocked Users</DialogTitle> <DialogTitle>Blocked Users</DialogTitle>
<DialogContent sx={{ <DialogContent
padding: '20px' sx={{
}}> padding: "20px",
}}
>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
@ -49,39 +156,42 @@ export const BlockedUsersModal = ({ close }) => {
}} }}
> >
<TextField <TextField
placeholder="Name" placeholder="Name or address"
value={value} value={value}
onChange={(e) => { onChange={(e) => {
setValue(e.target.value); setValue(e.target.value);
}} }}
/> />
<Button variant="contained" onClick={async ()=> { <Button
try { sx={{
if(!value) return flexShrink: 0,
await addToBlockList(undefined, value) }}
fetchBlockedUsers() variant="contained"
setHasChanged(true) onClick={blockUser}
} catch (error) { >
console.error(error) Block
} </Button>
}}>Block</Button>
</Box> </Box>
{Object.entries(blockedUsers?.addresses).length > 0 && ( {Object.entries(blockedUsers?.addresses).length > 0 && (
<> <>
<Spacer height="20px" /> <Spacer height="20px" />
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Blocked Users for Chat ( addresses ) Blocked addresses- blocks processing of txs
</DialogContentText> </DialogContentText>
<Spacer height="10px" /> <Spacer height="10px" />
<Button variant="contained" size="small" onClick={getNames}>Fetch names</Button>
<Spacer height="10px" />
</> </>
)} )}
<Box sx={{ <Box
display: 'flex', sx={{
flexDirection: 'column', display: "flex",
gap: '10px' flexDirection: "column",
}}> gap: "10px",
}}
>
{Object.entries(blockedUsers?.addresses || {})?.map( {Object.entries(blockedUsers?.addresses || {})?.map(
([key, value]) => { ([key, value]) => {
return ( return (
@ -90,18 +200,22 @@ export const BlockedUsersModal = ({ close }) => {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
gap: "10px", gap: "10px",
width: '100%', width: "100%",
justifyContent: 'space-between' justifyContent: "space-between",
}} }}
> >
<Typography>{key}</Typography> <Typography>{addressesWithNames[key] || key}</Typography>
<Button <Button
sx={{
flexShrink: 0,
}}
size="small"
variant="contained" variant="contained"
onClick={async () => { onClick={async () => {
try { try {
await removeBlockFromList(key, undefined); await removeBlockFromList(key, undefined);
setHasChanged(true); setHasChanged(true);
setValue('') setValue("");
fetchBlockedUsers(); fetchBlockedUsers();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -119,17 +233,19 @@ export const BlockedUsersModal = ({ close }) => {
<> <>
<Spacer height="20px" /> <Spacer height="20px" />
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Blocked Users for QDN and Chat (names) Blocked names for QDN
</DialogContentText> </DialogContentText>
<Spacer height="10px" /> <Spacer height="10px" />
</> </>
)} )}
<Box sx={{ <Box
display: 'flex', sx={{
flexDirection: 'column', display: "flex",
gap: '10px' flexDirection: "column",
}}> gap: "10px",
}}
>
{Object.entries(blockedUsers?.names || {})?.map(([key, value]) => { {Object.entries(blockedUsers?.names || {})?.map(([key, value]) => {
return ( return (
<Box <Box
@ -137,12 +253,16 @@ export const BlockedUsersModal = ({ close }) => {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
gap: "10px", gap: "10px",
width: '100%', width: "100%",
justifyContent: 'space-between' justifyContent: "space-between",
}} }}
> >
<Typography>{key}</Typography> <Typography>{key}</Typography>
<Button <Button
size="small"
sx={{
flexShrink: 0,
}}
variant="contained" variant="contained"
onClick={async () => { onClick={async () => {
try { try {
@ -177,14 +297,65 @@ export const BlockedUsersModal = ({ close }) => {
variant="contained" variant="contained"
onClick={() => { onClick={() => {
if (hasChanged) { if (hasChanged) {
executeEvent('updateChatMessagesWithBlocks', true) executeEvent("updateChatMessagesWithBlocks", true);
} }
close() setIsOpenBlockedModal(false);
}} }}
> >
close close
</Button> </Button>
</DialogActions> </DialogActions>
<Dialog
open={isShow}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Decide what to block"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Blocking {message?.userName || message?.userAddress}
</DialogContentText>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: '10px',
marginTop: '20px'
}}>
<InfoIcon sx={{
color: 'fff'
}}/> <Typography>Choose "block txs" or "all" to block chat messages </Typography>
</Box>
</DialogContent>
<DialogActions>
<Button
variant="contained"
onClick={() => {
onOk("address");
}}
>
Block txs
</Button>
<Button
variant="contained"
onClick={() => {
onOk("name");
}}
>
Block QDN data
</Button>
<Button
variant="contained"
onClick={() => {
onOk("both");
}}
>
Block All
</Button>
</DialogActions>
</Dialog>
</Dialog> </Dialog>
); );
}; };

View File

@ -95,13 +95,15 @@ import { formatEmailDate } from "./QMailMessages";
import LockIcon from '@mui/icons-material/Lock'; import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
import { useRecoilState, useSetRecoilState } from "recoil"; import { useRecoilState, useSetRecoilState } from "recoil";
import { addressInfoControllerAtom, groupsPropertiesAtom, selectedGroupIdAtom } from "../../atoms/global"; import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time"; import { sortArrayByTimestampAndGroupName } from "../../utils/time";
import { AdminSpace } from "../Chat/AdminSpace"; import { AdminSpace } from "../Chat/AdminSpace";
import { HubsIcon } from "../../assets/Icons/HubsIcon"; import { HubsIcon } from "../../assets/Icons/HubsIcon";
import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
import { DesktopSideBar } from "../DesktopSideBar"; import { DesktopSideBar } from "../DesktopSideBar";
import BlockIcon from '@mui/icons-material/Block';
import PersonOffIcon from '@mui/icons-material/PersonOff';
import { BlockedUsersModal } from "./BlockedUsersModal"; import { BlockedUsersModal } from "./BlockedUsersModal";
import { WalletsAppWrapper } from "./WalletsAppWrapper"; import { WalletsAppWrapper } from "./WalletsAppWrapper";
@ -477,6 +479,8 @@ export const Group = ({
const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [groupAnnouncements, setGroupAnnouncements] = React.useState({});
const [defaultThread, setDefaultThread] = React.useState(null); const [defaultThread, setDefaultThread] = React.useState(null);
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom)
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState("");
const [drawerMode, setDrawerMode] = React.useState("groups"); const [drawerMode, setDrawerMode] = React.useState("groups");
@ -484,7 +488,6 @@ export const Group = ({
const [mobileViewMode, setMobileViewMode] = useState("home"); const [mobileViewMode, setMobileViewMode] = useState("home");
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(""); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState("");
const isFocusedRef = useRef(true); const isFocusedRef = useRef(true);
const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false);
const timestampEnterDataRef = useRef({}); const timestampEnterDataRef = useRef({});
const selectedGroupRef = useRef(null); const selectedGroupRef = useRef(null);
const selectedDirectRef = useRef(null); const selectedDirectRef = useRef(null);
@ -2255,7 +2258,7 @@ export const Group = ({
padding: '10px' padding: '10px'
}} }}
> >
<BlockIcon <PersonOffIcon
sx={{ sx={{
color: "white", color: "white",
}} }}
@ -2773,11 +2776,9 @@ export const Group = ({
)} )}
</div> </div>
)} )}
{isOpenBlockedUserModal && (
<BlockedUsersModal close={()=> { <BlockedUsersModal />
setIsOpenBlockedUserModal(false)
}} />
)}
{selectedDirect && !newChat && ( {selectedDirect && !newChat && (
<> <>
<Box <Box

View File

@ -171,12 +171,10 @@ useEffect(()=> {
onClick={async () => { onClick={async () => {
try { try {
setIsLoading(true) setIsLoading(true)
if(isAlreadyBlocked === true){ executeEvent("blockUserFromOutside", {
await removeBlockFromList(address, name) user: address
} else if(isAlreadyBlocked === false) { })
await addToBlockList(address, name)
}
executeEvent('updateChatMessagesWithBlocks', true)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally { } finally {

View File

@ -1,5 +1,5 @@
import { banFromGroup, gateways, getApiKeyFromStorage } from "./background"; import { banFromGroup, gateways, getApiKeyFromStorage } from "./background";
import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createBuyOrder, createGroupRequest, createPoll, createSellOrder, 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, 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, createBuyOrder, createGroupRequest, createPoll, createSellOrder, 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, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sellNameRequest, sendChatMessage, sendCoin, setCurrentForeignServer, showPdfReader, signTransaction, updateForeignFee, updateGroupRequest, updateNameRequest, voteOnPoll } from "./qortalRequests/get";
export const listOfAllQortalRequests = [ export const listOfAllQortalRequests = [
'GET_USER_ACCOUNT', 'GET_USER_ACCOUNT',
@ -82,7 +82,11 @@ import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banF
'GET_NODE_INFO', 'GET_NODE_INFO',
'GET_NODE_STATUS', 'GET_NODE_STATUS',
'GET_ARRR_SYNC_STATUS', 'GET_ARRR_SYNC_STATUS',
'SHOW_PDF_READER' 'SHOW_PDF_READER',
'UPDATE_GROUP',
'SELL_NAME',
'CANCEL_SELL_NAME',
'BUY_NAME'
] ]
// Promisify chrome.storage.local.get // Promisify chrome.storage.local.get
@ -851,6 +855,55 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => {
break; break;
} }
case "UPDATE_GROUP" : {
const data = request.payload;
updateGroupRequest(data, isFromExtension)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
case "BUY_NAME" : {
const data = request.payload;
buyNameRequest(data, isFromExtension)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
case "SELL_NAME" : {
const data = request.payload;
sellNameRequest(data, isFromExtension)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
case "CANCEL_SELL_NAME" : {
const data = request.payload;
cancelSellNameRequest(data, isFromExtension)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
} }
} }
return true; return true;

View File

@ -28,7 +28,11 @@ import {
makeAdmin, makeAdmin,
removeAdmin, removeAdmin,
cancelInvitationToGroup, cancelInvitationToGroup,
createGroup createGroup,
updateGroup,
buyName,
cancelSellName,
sellName
} from "../background"; } from "../background";
import { decryptGroupEncryption, getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption"; import { decryptGroupEncryption, getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption";
import { QORT_DECIMALS } from "../constants/constants"; import { QORT_DECIMALS } from "../constants/constants";
@ -59,6 +63,7 @@ import utils from "../utils/utils";
import { RequestQueueWithPromise } from "../utils/queue/queue"; import { RequestQueueWithPromise } from "../utils/queue/queue";
import { Sha256 } from "asmcrypto.js"; import { Sha256 } from "asmcrypto.js";
import ed2curve from "../deps/ed2curve"; import ed2curve from "../deps/ed2curve";
import { executeEvent } from "../utils/events";
export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10);
@ -1183,6 +1188,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: errorMsg, reason: errorMsg,
identifier: resource.identifier, identifier: resource.identifier,
service: resource.service,
}); });
continue; continue;
} }
@ -1191,6 +1197,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: errorMsg, reason: errorMsg,
identifier: resource.identifier, identifier: resource.identifier,
service: resource.service,
}); });
continue; continue;
} }
@ -1247,6 +1254,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: errorMsg, reason: errorMsg,
identifier: resource.identifier, identifier: resource.identifier,
service: resource.service,
}); });
continue; continue;
} }
@ -1273,7 +1281,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten
apiVersion: 2, apiVersion: 2,
withFee: true, withFee: true,
}, },
], false); ], true);
await new Promise((res) => { await new Promise((res) => {
setTimeout(() => { setTimeout(() => {
res(); res();
@ -1284,17 +1292,21 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: errorMsg, reason: errorMsg,
identifier: resource.identifier, identifier: resource.identifier,
service: resource.service,
}); });
} }
} catch (error) { } catch (error) {
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: error?.message || "Unknown error", reason: error?.message || "Unknown error",
identifier: resource.identifier, identifier: resource.identifier,
service: resource.service,
}); });
} }
} }
if (failedPublishesIdentifiers.length > 0) { if (failedPublishesIdentifiers.length > 0) {
const obj = {}; const obj = {
message: "Some resources have failed to publish.",
};
obj["error"] = { obj["error"] = {
unsuccessfulPublishes: failedPublishesIdentifiers, unsuccessfulPublishes: failedPublishesIdentifiers,
}; };
@ -2682,7 +2694,8 @@ export const sendCoin = async (data, isFromExtension) => {
text1: "Do you give this application permission to send coins?", text1: "Do you give this application permission to send coins?",
text2: `To: ${recipient}`, text2: `To: ${recipient}`,
highlightedText: `${amount} ${checkCoin}`, highlightedText: `${amount} ${checkCoin}`,
fee: fee fee: fee,
confirmCheckbox: true
}, isFromExtension); }, isFromExtension);
const { accepted } = resPermission; const { accepted } = resPermission;
@ -3884,6 +3897,11 @@ export const registerNameRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 fee = await getFee("REGISTER_NAME");
const resPermission = await getUserPermission( const resPermission = await getUserPermission(
{ {
@ -3897,7 +3915,7 @@ export const registerNameRequest = async (data, isFromExtension) => {
const { accepted } = resPermission; const { accepted } = resPermission;
if (accepted) { if (accepted) {
const name = data.name const name = data.name
const description = data?.description const description = data?.description || ""
const response = await registerName({ name, description }); const response = await registerName({ name, description });
return response return response
@ -3914,9 +3932,14 @@ export const updateNameRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 oldName = data.oldName
const newName = data.newName const newName = data.newName
const description = data?.description const description = data?.description || ""
const fee = await getFee("UPDATE_NAME"); const fee = await getFee("UPDATE_NAME");
const resPermission = await getUserPermission( const resPermission = await getUserPermission(
{ {
@ -3945,6 +3968,11 @@ export const leaveGroupRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
let groupInfo = null; let groupInfo = null;
try { try {
@ -3985,6 +4013,11 @@ export const inviteToGroupRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.inviteeAddress const qortalAddress = data?.inviteeAddress
const inviteTime = data?.inviteTime const inviteTime = data?.inviteTime
@ -4034,6 +4067,11 @@ export const kickFromGroupRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
const reason = data?.reason const reason = data?.reason
@ -4083,6 +4121,11 @@ export const banFromGroupRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
const rBanTime = data?.banTime const rBanTime = data?.banTime
@ -4133,6 +4176,11 @@ export const cancelGroupBanRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
@ -4180,6 +4228,11 @@ export const addGroupAdminRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
@ -4227,6 +4280,11 @@ export const removeGroupAdminRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
@ -4274,6 +4332,11 @@ export const cancelGroupInviteRequest = async (data, isFromExtension) => {
missingFields.push(field); 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 groupId = data.groupId
const qortalAddress = data?.qortalAddress const qortalAddress = data?.qortalAddress
@ -4368,15 +4431,20 @@ export const decryptAESGCMRequest = async (data, isFromExtension) => {
}; };
export const createGroupRequest = 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[] = []; const missingFields: string[] = [];
requiredFields.forEach((field) => { requiredFields.forEach((field) => {
if (!data[field]) { if (data[field] !== undefined && data[field] !== null) {
missingFields.push(field); 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 groupName = data.groupName
const description = data?.description const description = data?.description || ""
const type = +data.type const type = +data.type
const approvalThreshold = +data?.approvalThreshold const approvalThreshold = +data?.approvalThreshold
const minBlock = +data?.minBlock const minBlock = +data?.minBlock
@ -4593,3 +4661,201 @@ export const getArrrSyncStatus = async () => {
throw new Error(error?.message || "Error in retrieving arrr sync status"); throw new Error(error?.message || "Error in retrieving arrr sync status");
} }
}; };
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 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");
}
};

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -20,17 +20,25 @@ import DeployAtTransaction from './DeployAtTransaction.js'
import RewardShareTransaction from './RewardShareTransaction.js' import RewardShareTransaction from './RewardShareTransaction.js'
import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js' import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js'
import UpdateNameTransaction from './UpdateNameTransaction.js' import UpdateNameTransaction from './UpdateNameTransaction.js'
import SellNameTransacion from './SellNameTransacion.js'
import CancelSellNameTransacion from './CancelSellNameTransacion.js'
import BuyNameTransacion from './BuyNameTransacion.js'
import UpdateGroupTransaction from './UpdateGroupTransaction.js'
export const transactionTypes = { export const transactionTypes = {
3: RegisterNameTransaction, 3: RegisterNameTransaction,
4: UpdateNameTransaction, 4: UpdateNameTransaction,
2: PaymentTransaction, 2: PaymentTransaction,
5: SellNameTransacion,
6: CancelSellNameTransacion,
7: BuyNameTransacion,
8: CreatePollTransaction, 8: CreatePollTransaction,
9: VoteOnPollTransaction, 9: VoteOnPollTransaction,
16: DeployAtTransaction, 16: DeployAtTransaction,
18: ChatTransaction, 18: ChatTransaction,
181: GroupChatTransaction, 181: GroupChatTransaction,
22: CreateGroupTransaction, 22: CreateGroupTransaction,
23: UpdateGroupTransaction,
24: AddGroupAdminTransaction, 24: AddGroupAdminTransaction,
25: RemoveGroupAdminTransaction, 25: RemoveGroupAdminTransaction,
26: GroupBanTransaction, 26: GroupBanTransaction,