diff --git a/src/background-cases.ts b/src/background-cases.ts index 2500434..84a7151 100644 --- a/src/background-cases.ts +++ b/src/background-cases.ts @@ -57,6 +57,7 @@ import { import { decryptGroupEncryption, encryptAndPublishSymmetricKeyGroupChat, publishGroupEncryptedResource, publishOnQDN } from "./backgroundFunctions/encryption"; import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes"; import { encryptSingle } from "./qdn/encryption/group-encryption"; +import { _createPoll, _voteOnPoll } from "./qortalRequests/get"; import { getData, storeData } from "./utils/chromeStorage"; export function versionCase(request, event) { @@ -772,6 +773,67 @@ export async function registerNameCase(request, event) { ); } } +export async function createPollCase(request, event) { + try { + console.log('request', event) + const { pollName, pollDescription, pollOptions } = request.payload; + const resCreatePoll = await _createPoll( + { + pollName, + pollDescription, + options: pollOptions, + }, + true, + true // skip permission + ); + + event.source.postMessage( + { + requestId: request.requestId, + action: "registerName", + payload: resCreatePoll, + type: "backgroundMessageResponse", + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: "registerName", + error: error?.message, + type: "backgroundMessageResponse", + }, + event.origin + ); + } +} +export async function voteOnPollCase(request, event) { + try { + const res = await _voteOnPoll(request.payload, true, true); + + + event.source.postMessage( + { + requestId: request.requestId, + action: "registerName", + payload: res, + type: "backgroundMessageResponse", + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: "registerName", + error: error?.message, + type: "backgroundMessageResponse", + }, + event.origin + ); + } +} export async function makeAdminCase(request, event) { try { diff --git a/src/background.ts b/src/background.ts index b634fae..c782917 100644 --- a/src/background.ts +++ b/src/background.ts @@ -45,6 +45,7 @@ import { checkLocalCase, clearAllNotificationsCase, createGroupCase, + createPollCase, decryptDirectCase, decryptGroupEncryptionCase, decryptSingleCase, @@ -93,6 +94,7 @@ import { userInfoCase, validApiCase, versionCase, + voteOnPollCase, } from "./background-cases"; import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage"; // import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch'; @@ -2893,7 +2895,12 @@ function setupMessageListener() { case "registerName": registerNameCase(request, event); break; - + case "createPoll": + createPollCase(request, event); + break; + case "voteOnPoll": + voteOnPollCase(request, event); + break; case "makeAdmin": makeAdminCase(request, event); break; diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 0ac5fa0..1cd6c18 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -103,28 +103,30 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { getRating(app?.name, app?.service); }, [getRating, app?.name]); - const rateFunc = async (event, newValue) => { + const rateFunc = async (event, chosenValue, currentValue) => { try { + const newValue = chosenValue || currentValue + console.log('event', newValue) if (!myName) throw new Error("You need a name to rate."); if (!app?.name) return; - const fee = await getFee("ARBITRARY"); + const fee = await getFee("CREATE_POLL"); await show({ - message: `Would you like to rate this app a rating of ${newValue}?`, + message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`, publishFee: fee.fee + " QORT", }); - + console.log('hasPublishedRating', hasPublishedRating) if (hasPublishedRating === false) { const pollName = `app-library-${app.service}-rating-${app.name}`; const pollOptions = [`1, 2, 3, 4, 5, initialValue-${newValue}`]; await new Promise((res, rej) => { - window.sendMessage("CREATE_POLL", { - payload: { + window.sendMessage("createPoll", { + pollName: pollName, pollDescription: `Rating for ${app.service} ${app.name}`, pollOptions: pollOptions, pollOwnerAddress: myName, - }, + }, 60000) .then((response) => { if (response.error) { @@ -147,17 +149,19 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { }); } else { const pollName = `app-library-${app.service}-rating-${app.name}`; + const optionIndex = pollInfo?.pollOptions.findIndex( (option) => +option.optionName === +newValue ); + console.log('optionIndex', optionIndex, newValue) if (isNaN(optionIndex) || optionIndex === -1) throw new Error("Cannot find rating option"); await new Promise((res, rej) => { - window.sendMessage("VOTE_ON_POLL", { - payload: { + window.sendMessage("voteOnPoll", { + pollName: pollName, optionIndex, - }, + }, 60000) .then((response) => { if (response.error) { @@ -180,9 +184,10 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { }); } } catch (error) { + console.log('error', error) setInfoSnack({ type: "error", - message: error.message || "An error occurred while trying to rate.", + message: error?.message || "Unable to rate", }); setOpenSnack(true); } @@ -212,9 +217,8 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { rateFunc(event, rating, value)} precision={1} - readOnly={hasPublishedRating === null} size="small" icon={} emptyIcon={} diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 5e6b69a..3aae721 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -182,7 +182,7 @@ const UIQortalRequests = [ 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', - 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY', 'ADMIN_ACTION', 'SIGN_TRANSACTION' + 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'OPEN_NEW_TAB' ]; diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx index 673aa02..34b2a82 100644 --- a/src/components/Chat/MessageDisplay.tsx +++ b/src/components/Chat/MessageDisplay.tsx @@ -3,7 +3,7 @@ import DOMPurify from 'dompurify'; import './styles.css'; import { executeEvent } from '../../utils/events'; -const extractComponents = (url) => { +export const extractComponents = (url) => { if (!url || !url.startsWith("qortal://")) { // Check if url exists and starts with "qortal://" return null; } diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index d7fb583..5520e2c 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,5 +1,5 @@ import { gateways, getApiKeyFromStorage } from "./background"; -import { addForeignServer, addListItems, adminAction, cancelSellOrder, createBuyOrder, createPoll, createSellOrder, decryptData, deleteListItems, deployAt, encryptData, getCrossChainServerInfo, getDaySummary, getForeignFee, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getWalletBalance, joinGroup, publishMultipleQDNResources, publishQDNResource, removeForeignServer, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, voteOnPoll } from "./qortalRequests/get"; +import { addForeignServer, addListItems, adminAction, cancelSellOrder, createBuyOrder, createPoll, createSellOrder, decryptData, deleteListItems, deployAt, encryptData, getCrossChainServerInfo, getDaySummary, getForeignFee, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getWalletBalance, joinGroup, openNewTab, publishMultipleQDNResources, publishQDNResource, removeForeignServer, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, voteOnPoll } from "./qortalRequests/get"; import { getData, storeData } from "./utils/chromeStorage"; @@ -276,7 +276,7 @@ export const isRunningGateway = async ()=> { case "SEND_CHAT_MESSAGE": { try { - const res = await sendChatMessage(request.payload, isFromExtension); + const res = await sendChatMessage(request.payload, isFromExtension, appInfo); event.source.postMessage({ requestId: request.requestId, action: request.action, @@ -710,6 +710,25 @@ export const isRunningGateway = async ()=> { } break; } + case "OPEN_NEW_TAB": { + try { + const res = await openNewTab(request.payload, isFromExtension) + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + 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 766c548..295c667 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -19,6 +19,7 @@ import { } from "../background"; import { getNameInfo } from "../backgroundFunctions/encryption"; import { showSaveFilePicker } from "../components/Apps/useQortalMessageListener"; +import { extractComponents } from "../components/Chat/MessageDisplay"; import { QORT_DECIMALS } from "../constants/constants"; import Base58 from "../deps/Base58"; import nacl from "../deps/nacl-fast"; @@ -40,6 +41,7 @@ import TradeBotCreateRequest from "../transactions/TradeBotCreateRequest"; import DeleteTradeOffer from "../transactions/TradeBotDeleteRequest"; import signTradeBotTransaction from "../transactions/signTradeBotTransaction"; import { createTransaction } from "../transactions/transactions"; +import { executeEvent } from "../utils/events"; import { mimeToExtensionMap } from "../utils/memeTypes"; import utils from "../utils/utils"; @@ -61,25 +63,28 @@ function roundUpToDecimals(number, decimals = 8) { return Math.ceil(+number * factor) / factor; } -const _createPoll = async ( +export const _createPoll = async ( { pollName, pollDescription, options }, - isFromExtension + isFromExtension, skipPermission ) => { const fee = await getFee("CREATE_POLL"); + let resPermission = {} + if(!skipPermission){ + resPermission = await getUserPermission( + { + text1: "You are requesting to create the poll below:", + text2: `Poll: ${pollName}`, + text3: `Description: ${pollDescription}`, + text4: `Options: ${options?.join(", ")}`, + fee: fee.fee, + }, + isFromExtension + ); + } + + const { accepted = false } = resPermission; - const resPermission = await getUserPermission( - { - text1: "You are requesting to create the poll below:", - text2: `Poll: ${pollName}`, - text3: `Description: ${pollDescription}`, - text4: `Options: ${options?.join(", ")}`, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { + if (accepted || skipPermission) { const wallet = await getSaveWallet(); const address = wallet.address0; const resKeyPair = await getKeyPair(); @@ -168,24 +173,27 @@ const _deployAt = async ( } }; -const _voteOnPoll = async ( +export const _voteOnPoll = async ( { pollName, optionIndex, optionName }, - isFromExtension + isFromExtension, skipPermission ) => { const fee = await getFee("VOTE_ON_POLL"); + let resPermission = {} + if(!skipPermission){ + resPermission = await getUserPermission( + { + text1: "You are being requested to vote on the poll below:", + text2: `Poll: ${pollName}`, + text3: `Option: ${optionName}`, + fee: fee.fee, + }, + isFromExtension + ); + } + + const { accepted = false } = resPermission; - const resPermission = await getUserPermission( - { - text1: "You are being requested to vote on the poll below:", - text2: `Poll: ${pollName}`, - text3: `Option: ${optionName}`, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { + if (accepted || skipPermission) { const wallet = await getSaveWallet(); const address = wallet.address0; const resKeyPair = await getKeyPair(); @@ -1035,23 +1043,40 @@ export const createPoll = async (data, isFromExtension) => { } }; -export const sendChatMessage = async (data, isFromExtension) => { - const message = data.message; +export const sendChatMessage = async (data, isFromExtension, appInfo) => { + const message = data?.message; + const fullMessageObject = data?.fullMessageObject const recipient = data.destinationAddress; const groupId = data.groupId; const isRecipient = !groupId; - const resPermission = await getUserPermission( + + const value = + (await getPermission(`qAPPSendChatMessage-${appInfo?.name}`)) || false; +let skip = false; +if (value) { + skip = true; +} +let resPermission; +if (!skip) { + 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 ? "..." : ""}`, + checkbox1: { + value: false, + label: "Always allow chat messages from this app", + }, }, isFromExtension ); - - const { accepted } = resPermission; - if (accepted) { + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission && accepted) { + setPermission(`qAPPSendChatMessage-${appInfo?.name}`, checkbox1); + } + if (accepted || skip) { const tiptapJson = { type: "doc", content: [ @@ -1066,7 +1091,7 @@ export const sendChatMessage = async (data, isFromExtension) => { }, ], }; - const messageObject = { + const messageObject = fullMessageObject ? fullMessageObject : { messageText: tiptapJson, images: [""], repliedTo: "", @@ -2862,6 +2887,37 @@ export const cancelSellOrder = async (data, isFromExtension) => { } }; +export const openNewTab = async (data, isFromExtension) => { + const requiredFields = [ + "qortalLink", + ]; + 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 res = extractComponents(data.qortalLink); + if (res) { + const { service, name, identifier, path } = res; + if(!service && !name) throw new Error('Invalid qortal link') + executeEvent("addTab", { data: { service, name, identifier, path } }); + executeEvent("open-apps-mode", { }); + return true + } else { + throw new Error("Invalid qortal link") + } + + + +}; + export const adminAction = async (data, isFromExtension) => { const requiredFields = ["type"]; const missingFields: string[] = [];