join group qortalrequest

This commit is contained in:
PhilReact 2024-10-15 23:51:14 +03:00
parent 470b554822
commit 638c673845
5 changed files with 438 additions and 174 deletions

View File

@ -729,7 +729,7 @@ async function storeFilesInIndexedDB(obj) {
const UIQortalRequests = ['GET_USER_ACCOUNT', 'ENCRYPT_DATA', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL'] const UIQortalRequests = ['GET_USER_ACCOUNT', 'ENCRYPT_DATA', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL', 'SEND_CHAT_MESSAGE', 'JOIN_GROUP']
if (!window.hasAddedQortalListener) { if (!window.hasAddedQortalListener) {
console.log("Listener added"); console.log("Listener added");

View File

@ -1896,6 +1896,7 @@ function App() {
{messageQortalRequest?.text2} {messageQortalRequest?.text2}
</TextP> </TextP>
</Box> </Box>
<Spacer height="15px" />
</> </>
)} )}
{messageQortalRequest?.text3 && ( {messageQortalRequest?.text3 && (
@ -1947,8 +1948,23 @@ function App() {
/> />
)} )}
<Spacer height="15px" /> <Spacer height="15px" />
<TextP
sx={{
textAlign: "center",
lineHeight: 1.2,
fontSize: "16px",
fontWeight: 700,
maxWidth: "90%",
}}
>
{messageQortalRequest?.highlightedText}
</TextP>
{messageQortalRequest?.fee && ( {messageQortalRequest?.fee && (
<> <>
<Spacer height="15px" />
<TextP <TextP
sx={{ sx={{
textAlign: "center", textAlign: "center",
@ -1964,17 +1980,6 @@ function App() {
</> </>
)} )}
<TextP
sx={{
textAlign: "center",
lineHeight: 1.2,
fontSize: "16px",
fontWeight: 700,
maxWidth: "90%",
}}
>
{messageQortalRequest?.highlightedText}
</TextP>
{messageQortalRequest?.checkbox1 && ( {messageQortalRequest?.checkbox1 && (
<Box <Box
sx={{ sx={{

View File

@ -1009,7 +1009,7 @@ async function getTradesInfo(qortalAtAddresses) {
return trades; // Return the array of trade info objects return trades; // Return the array of trade info objects
} }
async function getBalanceInfo() { export async function getBalanceInfo() {
const wallet = await getSaveWallet(); const wallet = await getSaveWallet();
const address = wallet.address0; const address = wallet.address0;
const validApi = await getBaseApi(); const validApi = await getBaseApi();
@ -1384,7 +1384,7 @@ async function decryptWallet({ password, wallet, walletVersion }) {
} }
} }
async function signChatFunc(chatBytesArray, chatNonce, customApi, keyPair) { export async function signChatFunc(chatBytesArray, chatNonce, customApi, keyPair) {
let response; let response;
try { try {
const signedChatBytes = signChat(chatBytesArray, chatNonce, keyPair); const signedChatBytes = signChat(chatBytesArray, chatNonce, keyPair);
@ -1409,7 +1409,7 @@ function sbrk(size, heap) {
return old; return old;
} }
const computePow = async ({ chatBytes, path, difficulty }) => { export const computePow = async ({ chatBytes, path, difficulty }) => {
let response = null; let response = null;
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { const _chatBytesArray = Object.keys(chatBytes).map(function (key) {
@ -1973,7 +1973,7 @@ async function leaveGroup({ groupId }) {
return res; return res;
} }
async function joinGroup({ groupId }) { export async function joinGroup({ groupId }) {
const wallet = await getSaveWallet(); const wallet = await getSaveWallet();
const address = wallet.address0; const address = wallet.address0;
const lastReference = await getLastRef(); const lastReference = await getLastRef();

View File

@ -1,4 +1,4 @@
import { addListItems, createPoll, decryptData, deleteListItems, encryptData, getListItems, getUserAccount, publishMultipleQDNResources, publishQDNResource, sendCoin, voteOnPoll } from "./qortalRequests/get"; import { addListItems, createPoll, decryptData, deleteListItems, encryptData, getListItems, getUserAccount, joinGroup, publishMultipleQDNResources, publishQDNResource, sendChatMessage, sendCoin, voteOnPoll } from "./qortalRequests/get";
@ -192,6 +192,32 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => {
break; break;
} }
case "SEND_CHAT_MESSAGE": {
const data = request.payload;
console.log('data', data)
sendChatMessage(data)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
case "JOIN_GROUP": {
const data = request.payload;
console.log('data', data)
joinGroup(data)
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
});
break;
}
case "SEND_COIN": { case "SEND_COIN": {
const data = request.payload; const data = request.payload;
const requiredFields = ["coin", "destinationAddress", "amount"]; const requiredFields = ["coin", "destinationAddress", "amount"];

View File

@ -1,11 +1,15 @@
import { import {
computePow,
createEndpoint, createEndpoint,
getBalanceInfo,
getFee, getFee,
getKeyPair, getKeyPair,
getLastRef, getLastRef,
getSaveWallet, getSaveWallet,
processTransactionVersion2, processTransactionVersion2,
removeDuplicateWindow, removeDuplicateWindow,
signChatFunc,
joinGroup as joinGroupFunc
} from "../background"; } from "../background";
import { getNameInfo } from "../backgroundFunctions/encryption"; import { getNameInfo } from "../backgroundFunctions/encryption";
import Base58 from "../deps/Base58"; import Base58 from "../deps/Base58";
@ -22,16 +26,14 @@ import { getPermission, setPermission } from "../qortalRequests";
import { createTransaction } from "../transactions/transactions"; import { createTransaction } from "../transactions/transactions";
import { fileToBase64 } from "../utils/fileReading"; import { fileToBase64 } from "../utils/fileReading";
const _createPoll = async (pollName, pollDescription, options) => { const _createPoll = async (pollName, pollDescription, options) => {
const fee = await getFee("CREATE_POLL"); const fee = await getFee("CREATE_POLL");
const resPermission = await getUserPermission({ const resPermission = await getUserPermission({
text1: "You are requesting to create the poll below:", text1: "You are requesting to create the poll below:",
text2: `Poll: ${pollName}`, text2: `Poll: ${pollName}`,
text3: `Description: ${pollDescription}`, text3: `Description: ${pollDescription}`,
text4: `Options: ${options?.join(', ')}`, text4: `Options: ${options?.join(", ")}`,
fee: fee.fee, fee: fee.fee,
}); });
const { accepted } = resPermission; const { accepted } = resPermission;
@ -47,7 +49,7 @@ const _createPoll = async (pollName, pollDescription, options) => {
privateKey: uint8PrivateKey, privateKey: uint8PrivateKey,
publicKey: uint8PublicKey, publicKey: uint8PublicKey,
}; };
let lastRef = await getLastRef() let lastRef = await getLastRef();
const tx = await createTransaction(8, keyPair, { const tx = await createTransaction(8, keyPair, {
fee: fee.fee, fee: fee.fee,
@ -55,7 +57,7 @@ const _createPoll = async (pollName, pollDescription, options) => {
rPollName: pollName, rPollName: pollName,
rPollDesc: pollDescription, rPollDesc: pollDescription,
rOptions: options, rOptions: options,
lastReference: lastRef lastReference: lastRef,
}); });
const signedBytes = Base58.encode(tx.signedBytes); const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes); const res = await processTransactionVersion2(signedBytes);
@ -65,11 +67,9 @@ const _createPoll = async (pollName, pollDescription, options) => {
} else { } else {
throw new Error("User declined request"); throw new Error("User declined request");
} }
};
}
const _voteOnPoll = async (pollName, optionIndex, optionName) => { const _voteOnPoll = async (pollName, optionIndex, optionName) => {
const fee = await getFee("VOTE_ON_POLL"); const fee = await getFee("VOTE_ON_POLL");
const resPermission = await getUserPermission({ const resPermission = await getUserPermission({
@ -91,14 +91,14 @@ const _voteOnPoll =async (pollName, optionIndex, optionName)=> {
privateKey: uint8PrivateKey, privateKey: uint8PrivateKey,
publicKey: uint8PublicKey, publicKey: uint8PublicKey,
}; };
let lastRef = await getLastRef() let lastRef = await getLastRef();
const tx = await createTransaction(9, keyPair, { const tx = await createTransaction(9, keyPair, {
fee: fee.fee, fee: fee.fee,
voterAddress: address, voterAddress: address,
rPollName: pollName, rPollName: pollName,
rOptionIndex: optionIndex, rOptionIndex: optionIndex,
lastReference: lastRef lastReference: lastRef,
}); });
const signedBytes = Base58.encode(tx.signedBytes); const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes); const res = await processTransactionVersion2(signedBytes);
@ -108,33 +108,26 @@ const _voteOnPoll =async (pollName, optionIndex, optionName)=> {
} else { } else {
throw new Error("User declined request"); throw new Error("User declined request");
} }
};
}
function getFileFromContentScript(fileId, sender) { function getFileFromContentScript(fileId, sender) {
console.log('sender', sender) console.log("sender", sender);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.tabs.sendMessage( chrome.tabs.sendMessage(
sender.tab.id, sender.tab.id,
{ action: "getFileFromIndexedDB", fileId: fileId }, { action: "getFileFromIndexedDB", fileId: fileId },
(response) => { (response) => {
console.log('response2', response) console.log("response2", response);
if (response && response.result) { if (response && response.result) {
resolve(response.result); resolve(response.result);
} else { } else {
reject(response?.error || "Failed to retrieve file"); reject(response?.error || "Failed to retrieve file");
} }
} }
); );
}); });
} }
async function getUserPermission(payload: any) { async function getUserPermission(payload: any) {
function waitForWindowReady(windowId) { function waitForWindowReady(windowId) {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -669,9 +662,13 @@ export const publishMultipleQDNResources = async (data: any, sender) => {
.map( .map(
(resource) => ` (resource) => `
<div class="resource-container"> <div class="resource-container">
<div class="resource-detail"><span>Service:</span> ${resource.service}</div> <div class="resource-detail"><span>Service:</span> ${
resource.service
}</div>
<div class="resource-detail"><span>Name:</span> ${resource.name}</div> <div class="resource-detail"><span>Name:</span> ${resource.name}</div>
<div class="resource-detail"><span>Identifier:</span> ${resource.identifier}</div> <div class="resource-detail"><span>Identifier:</span> ${
resource.identifier
}</div>
${ ${
resource.filename resource.filename
? `<div class="resource-detail"><span>Filename:</span> ${resource.filename}</div>` ? `<div class="resource-detail"><span>Filename:</span> ${resource.filename}</div>`
@ -687,12 +684,12 @@ export const publishMultipleQDNResources = async (data: any, sender) => {
fee: fee.fee * resources.length, fee: fee.fee * resources.length,
}); });
const { accepted } = resPermission; const { accepted } = resPermission;
console.log('accepted', accepted) console.log("accepted", accepted);
if (!accepted) { if (!accepted) {
throw new Error("User declined request"); throw new Error("User declined request");
} }
let failedPublishesIdentifiers = []; let failedPublishesIdentifiers = [];
console.log('resources', resources) console.log("resources", resources);
for (const resource of resources) { for (const resource of resources) {
try { try {
const requiredFields = ["service"]; const requiredFields = ["service"];
@ -766,7 +763,6 @@ export const publishMultipleQDNResources = async (data: any, sender) => {
} }
try { try {
await publishData({ await publishData({
registeredName: encodeURIComponent(name), registeredName: encodeURIComponent(name),
file: data64, file: data64,
@ -799,7 +795,7 @@ export const publishMultipleQDNResources = async (data: any, sender) => {
}); });
} }
} catch (error) { } catch (error) {
console.log('error', error) console.log("error", error);
failedPublishesIdentifiers.push({ failedPublishesIdentifiers.push({
reason: "Unknown error", reason: "Unknown error",
identifier: resource.identifier, identifier: resource.identifier,
@ -817,21 +813,21 @@ export const publishMultipleQDNResources = async (data: any, sender) => {
}; };
export const voteOnPoll = async (data) => { export const voteOnPoll = async (data) => {
const requiredFields = ['pollName', 'optionIndex'] const requiredFields = ["pollName", "optionIndex"];
const missingFields: string[] = [] const missingFields: string[] = [];
requiredFields.forEach((field) => { requiredFields.forEach((field) => {
if (!data[field] && data[field] !== 0) { if (!data[field] && data[field] !== 0) {
missingFields.push(field) missingFields.push(field);
} }
}) });
if (missingFields.length > 0) { if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(', ') const missingFieldsString = missingFields.join(", ");
const errorMsg = `Missing fields: ${missingFieldsString}` const errorMsg = `Missing fields: ${missingFieldsString}`;
throw new Error(errorMsg) throw new Error(errorMsg);
} }
const pollName = data.pollName const pollName = data.pollName;
const optionIndex = data.optionIndex const optionIndex = data.optionIndex;
let pollInfo = null let pollInfo = null;
try { try {
const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`); const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`);
const response = await fetch(url); const response = await fetch(url);
@ -839,25 +835,233 @@ export const voteOnPoll = async (data) => {
pollInfo = await response.json(); pollInfo = await response.json();
} catch (error) { } catch (error) {
const errorMsg = (error && error.message) || 'Poll not found' const errorMsg = (error && error.message) || "Poll not found";
throw new Error(errorMsg) throw new Error(errorMsg);
} }
if (!pollInfo || pollInfo.error) { if (!pollInfo || pollInfo.error) {
const errorMsg = (pollInfo && pollInfo.message) || 'Poll not found' const errorMsg = (pollInfo && pollInfo.message) || "Poll not found";
throw new Error(errorMsg) throw new Error(errorMsg);
} }
try { try {
const optionName = pollInfo.pollOptions[optionIndex].optionName const optionName = pollInfo.pollOptions[optionIndex].optionName;
const resVoteOnPoll = await _voteOnPoll(pollName, optionIndex, optionName) const resVoteOnPoll = await _voteOnPoll(pollName, optionIndex, optionName);
return resVoteOnPoll return resVoteOnPoll;
} catch (error) { } catch (error) {
throw new Error(error?.message || "Failed to vote on the poll.");
throw new Error(error?.message || 'Failed to vote on the poll.')
} }
}; };
export const createPoll = async (data) => { export const createPoll = async (data) => {
const requiredFields = ['pollName', 'pollDescription', 'pollOptions', 'pollOwnerAddress'] const requiredFields = [
"pollName",
"pollDescription",
"pollOptions",
"pollOwnerAddress",
];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (!data[field]) {
missingFields.push(field);
}
});
if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(", ");
const errorMsg = `Missing fields: ${missingFieldsString}`;
throw new Error(errorMsg);
}
const pollName = data.pollName;
const pollDescription = data.pollDescription;
const pollOptions = data.pollOptions;
const pollOwnerAddress = data.pollOwnerAddress;
try {
const resCreatePoll = await _createPoll(
pollName,
pollDescription,
pollOptions,
pollOwnerAddress
);
return resCreatePoll;
} catch (error) {
throw new Error(error?.message || "Failed to created poll.");
}
};
export const sendChatMessage = async (data) => {
const message = data.message;
const recipient = data.destinationAddress;
const groupId = data.groupId;
const isRecipient = !groupId;
const resPermission = await getUserPermission({
text1: "Do you give this application permission to send this chat message?",
text2: `To: ${isRecipient ? recipient : `group ${groupId}`}`,
text3: `${message?.slice(0, 25)}${message?.length > 25 ? "..." : ""}`,
});
const { accepted } = resPermission;
if (accepted) {
const tiptapJson = {
type: "doc",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: message,
},
],
},
],
};
const messageObject = {
messageText: tiptapJson,
images: [""],
repliedTo: "",
version: 3,
};
try {
JSON.stringify(messageObject);
} catch (error) {
console.log('my error', error)
}
const stringifyMessageObject = JSON.stringify(messageObject);
const balance = await getBalanceInfo();
const hasEnoughBalance = +balance < 4 ? false : true;
if (!hasEnoughBalance) {
throw new Error("You need at least 4 QORT to send a message");
}
if (isRecipient && recipient) {
const url = await createEndpoint(`/addresses/publickey/${recipient}`);
const response = await fetch(url);
if (!response.ok)
throw new Error("Failed to fetch recipient's public key");
let key
let hasPublicKey;
let res
const contentType = response.headers.get("content-type");
// If the response is JSON, parse it as JSON
if (contentType && contentType.includes("application/json")) {
res = await response.json();
} else {
// Otherwise, treat it as plain text
res = await response.text();
}
console.log('res', res)
if (res?.error === 102) {
key = "";
hasPublicKey = false;
} else if (res !== false) {
key = res;
hasPublicKey = true;
} else {
key = "";
hasPublicKey = false;
}
if (!hasPublicKey && isRecipient) {
throw new Error(
"Cannot send an encrypted message to this user since they do not have their publickey on chain."
);
}
let _reference = new Uint8Array(64);
self.crypto.getRandomValues(_reference);
let sendTimestamp = Date.now();
let reference = Base58.encode(_reference);
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const difficulty = 8;
const tx = await createTransaction(18, keyPair, {
timestamp: sendTimestamp,
recipient: recipient,
recipientPublicKey: key,
hasChatReference: 0,
message: stringifyMessageObject,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 1,
isText: 1,
});
const path = chrome.runtime.getURL("memory-pow.wasm.full");
const { nonce, chatBytesArray } = await computePow({
chatBytes: tx.chatBytes,
path,
difficulty,
});
let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair);
if (_response?.error) {
throw new Error(_response?.message);
}
return _response;
} else if (!isRecipient && groupId) {
let _reference = new Uint8Array(64);
self.crypto.getRandomValues(_reference);
let reference = Base58.encode(_reference);
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const difficulty = 8;
const txBody = {
timestamp: Date.now(),
groupID: Number(groupId),
hasReceipient: 0,
hasChatReference: 0,
message: stringifyMessageObject,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 0, // Set default to not encrypted for groups
isText: 1,
}
const tx = await createTransaction(181, keyPair, txBody);
// if (!hasEnoughBalance) {
// throw new Error("Must have at least 4 QORT to send a chat message");
// }
const path = chrome.runtime.getURL("memory-pow.wasm.full");
const { nonce, chatBytesArray } = await computePow({
chatBytes: tx.chatBytes,
path,
difficulty,
});
let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair);
if (_response?.error) {
throw new Error(_response?.message);
}
return _response;
} else {
throw new Error("Please enter a recipient or groupId");
}
} else {
throw new Error("User declined add to list");
}
};
export const joinGroup = async (data) => {
const requiredFields = ['groupId']
const missingFields: string[] = [] const missingFields: string[] = []
requiredFields.forEach((field) => { requiredFields.forEach((field) => {
if (!data[field]) { if (!data[field]) {
@ -869,16 +1073,45 @@ export const voteOnPoll = async (data) => {
const errorMsg = `Missing fields: ${missingFieldsString}` const errorMsg = `Missing fields: ${missingFieldsString}`
throw new Error(errorMsg) throw new Error(errorMsg)
} }
const pollName = data.pollName let groupInfo = null
const pollDescription = data.pollDescription
const pollOptions = data.pollOptions
const pollOwnerAddress = data.pollOwnerAddress
try { try {
const resCreatePoll = await _createPoll(pollName, pollDescription, pollOptions, pollOwnerAddress)
return resCreatePoll const url = await createEndpoint(`/groups/${data.groupId}`);
const response = await fetch(url);
if (!response.ok) throw new Error("Failed to fetch group");
groupInfo = await response.json();
} catch (error) { } catch (error) {
throw new Error(error?.message || 'Failed to created poll.') const errorMsg = (error && error.message) || 'Group not found'
throw new Error(errorMsg)
} }
const fee = await getFee("JOIN_GROUP");
const resPermission = await getUserPermission({
text1: "Confirm joining the group:",
highlightedText: `${groupInfo.groupName}`,
fee: fee.fee
});
const { accepted } = resPermission;
if(accepted){
const groupId = data.groupId
if (!groupInfo || groupInfo.error) {
const errorMsg = (groupInfo && groupInfo.message) || 'Group not found'
throw new Error(errorMsg)
}
try {
const resJoinGroup = await joinGroupFunc({groupId})
return resJoinGroup
} catch (error) {
throw new Error(error?.message || 'Failed to join the group.')
}
} else {
throw new Error("User declined add to list");
}
}; };
export const sendCoin = async () => { export const sendCoin = async () => {