Browse Source

Merge pull request #206 from Philreact/feature/create-poll-and-vote

Feature/create poll and vote
master
QuickMythril 12 months ago committed by GitHub
parent
commit
f90e2c4241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      core/language/us.json
  2. 111
      crypto/api/transactions/polls/CreatePollTransaction.js
  3. 65
      crypto/api/transactions/polls/VoteOnPollTransaction.js
  4. 4
      crypto/api/transactions/transactions.js
  5. 8
      plugins/plugins/core/components/qdn-action-types.js
  6. 240
      plugins/plugins/core/qdn/browser/browser.src.js

8
core/language/us.json

@ -1024,7 +1024,13 @@
"rewarddialog6": "On pressing confirm, the rewardshare will be removed and the minting key will become invalid.",
"deployAtdialog1": "You are deploying the AT",
"deployAtdialog2": "On pressing confirm, the AT will be deployed!",
"deployAtdialog3": "Initial amount balance"
"deployAtdialog3": "Initial amount balance",
"votedialog1": "You are requesting to vote on the poll below:",
"votedialog2": "On pressing confirm, the vote request will be sent!",
"votedialog3": "You are requesting to create the poll below:",
"votedialog4": "Poll Description",
"votedialog5": "Options",
"votedialog6": "On pressing confirm, the poll will be created!"
},
"sponsorshipspage": {
"schange1": "Active Sponsorships",

111
crypto/api/transactions/polls/CreatePollTransaction.js

@ -0,0 +1,111 @@
'use strict'
import TransactionBase from '../TransactionBase.js'
import { QORT_DECIMALS } from '../../constants.js'
export default class CreatePollTransaction extends TransactionBase {
constructor() {
super()
this.type = 8
this._options = []
}
render(html) {
return html`
${this._votedialog3}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this._rPollName}</span>
</div>
${this._votedialog4}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this._rPollDesc}</span>
</div>
${this._votedialog5}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this._pollOptions.join(', ')}</span>
</div>
${this._votedialog6}
<div style="margin-top: 10px; font-weight: bold">
${this._feeDialog}: ${this._feeDisplay}
</div>
`
}
addOption(option) {
const optionBytes = this.constructor.utils.stringtoUTF8Array(option);
const optionLength = this.constructor.utils.int32ToBytes(optionBytes.length);
this._options.push({ length: optionLength, bytes: optionBytes });
}
set feeDialog(feeDialog){
this._feeDialog = feeDialog
}
set votedialog3(votedialog3) {
this._votedialog3 = votedialog3
}
set votedialog4(votedialog4) {
this._votedialog4 = votedialog4
}
set votedialog5(votedialog5) {
this._votedialog5 = votedialog5
}
set votedialog6(votedialog6) {
this._votedialog6 = votedialog6
}
set fee(fee) {
this._feeDisplay = fee
this._fee = fee * QORT_DECIMALS
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
}
set ownerAddress(ownerAddress) {
this._ownerAddress = ownerAddress instanceof Uint8Array ? ownerAddress : this.constructor.Base58.decode(ownerAddress)
}
set rPollName(rPollName) {
this._rPollName = rPollName
this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array(this._rPollName)
this._rPollNameLength = this.constructor.utils.int32ToBytes(this._rPollNameBytes.length);
}
set rPollDesc(rPollDesc) {
this._rPollDesc = rPollDesc
this._rPollDescBytes = this.constructor.utils.stringtoUTF8Array(this._rPollDesc.toLocaleLowerCase())
this._rPollDescLength = this.constructor.utils.int32ToBytes(this._rPollDescBytes.length)
}
set rOptions(rOptions) {
const optionsArray = rOptions[0].split(', ').map(opt => opt.trim());
this._pollOptions = optionsArray
for (let i = 0; i < optionsArray.length; i++) {
this.addOption(optionsArray[i]);
}
this._rNumberOfOptionsBytes = this.constructor.utils.int32ToBytes(optionsArray.length);
}
get params() {
const params = super.params
params.push(
this._ownerAddress,
this._rPollNameLength,
this._rPollNameBytes,
this._rPollDescLength,
this._rPollDescBytes,
this._rNumberOfOptionsBytes
)
// Push the dynamic options
console.log('this._options', this._options)
for (let i = 0; i < this._options.length; i++) {
params.push(this._options[i].length, this._options[i].bytes);
}
params.push(this._feeBytes);
return params
}
}

65
crypto/api/transactions/polls/VoteOnPollTransaction.js

@ -0,0 +1,65 @@
'use strict'
import TransactionBase from '../TransactionBase.js'
import { QORT_DECIMALS } from '../../constants.js'
export default class VoteOnPollTransaction extends TransactionBase {
constructor() {
super()
this.type = 9
}
render(html) {
return html`
${this._votedialog1}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this._rPollName}</span>
</div>
${this._votedialog2}
<div style="margin-top: 10px; font-weight: bold">
${this._feeDialog}: ${this._feeDisplay}
</div>
`
}
set feeDialog(feeDialog){
this._feeDialog = feeDialog
}
set votedialog1(votedialog1) {
this._votedialog1 = votedialog1
}
set votedialog2(votedialog2) {
this._votedialog2 = votedialog2
}
set fee(fee) {
this._feeDisplay = fee
this._fee = fee * QORT_DECIMALS
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
}
set rPollName(rPollName) {
this._rPollName = rPollName
this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array(this._rPollName)
this._rPollNameLength = this.constructor.utils.int32ToBytes(this._rPollNameBytes.length)
}
set rOptionIndex(rOptionIndex) {
this._rOptionIndex = rOptionIndex
this._rOptionIndexBytes = this.constructor.utils.int32ToBytes(this._rOptionIndex)
}
get params() {
const params = super.params
params.push(
this._rPollNameLength,
this._rPollNameBytes,
this._rOptionIndexBytes,
this._feeBytes
)
return params
}
}

4
crypto/api/transactions/transactions.js

@ -23,6 +23,8 @@ import RewardShareTransaction from './reward-share/RewardShareTransaction.js'
import RemoveRewardShareTransaction from './reward-share/RemoveRewardShareTransaction.js'
import TransferPrivsTransaction from './TransferPrivsTransaction.js'
import DeployAtTransaction from './DeployAtTransaction.js'
import VoteOnPollTransaction from './polls/VoteOnPollTransaction.js'
import CreatePollTransaction from './polls/CreatePollTransaction.js'
export const transactionTypes = {
2: PaymentTransaction,
@ -31,6 +33,8 @@ export const transactionTypes = {
5: SellNameTransacion,
6: CancelSellNameTransacion,
7: BuyNameTransacion,
8: CreatePollTransaction,
9: VoteOnPollTransaction,
16: DeployAtTransaction,
17: MessageTransaction,
18: ChatTransaction,

8
plugins/plugins/core/components/qdn-action-types.js

@ -59,4 +59,10 @@ export const OPEN_NEW_TAB = 'OPEN_NEW_TAB'
export const NOTIFICATIONS_PERMISSION = 'NOTIFICATIONS_PERMISSION'
//SEND_LOCAL_NOTIFICATION
export const SEND_LOCAL_NOTIFICATION = 'SEND_LOCAL_NOTIFICATION'
export const SEND_LOCAL_NOTIFICATION = 'SEND_LOCAL_NOTIFICATION'
//VOTE_ON_POLL
export const VOTE_ON_POLL= 'VOTE_ON_POLL'
//CREATE_POLL
export const CREATE_POLL= 'CREATE_POLL'

240
plugins/plugins/core/qdn/browser/browser.src.js

@ -496,6 +496,33 @@ class WebBrowser extends LitElement {
return qortFee
}
async unitVoteFee() {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
const url = `${nodeUrl}/transactions/unitfee?txType=VOTE_ON_POLL`
const response = await fetch(url)
if (!response.ok) {
throw new Error('Error when fetching vote fee');
}
const data = await response.json()
const joinFee = (Number(data) / 1e8).toFixed(8)
return joinFee
}
async unitCreatePollFee() {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
const url = `${nodeUrl}/transactions/unitfee?txType=CREATE_POLL`
const response = await fetch(url)
if (!response.ok) {
throw new Error('Error when fetching vote fee');
}
const data = await response.json()
const joinFee = (Number(data) / 1e8).toFixed(8)
return joinFee
}
async _joinGroup(groupId, groupName) {
const joinFeeInput = await this.unitJoinFee()
const getLastRef = async () => {
@ -610,6 +637,122 @@ class WebBrowser extends LitElement {
}
async _voteOnPoll(pollName, optionIndex) {
const voteFeeInput = await this.unitVoteFee()
const getLastRef = async () => {
let myRef = await parentEpml.request('apiCall', {
type: 'api',
url: `/addresses/lastreference/${this.selectedAddress.address}`
})
return myRef
};
const validateReceiver = async () => {
let lastRef = await getLastRef();
let myTransaction = await makeTransactionRequest(lastRef)
const res = getTxnRequestResponse(myTransaction)
return res
}
const makeTransactionRequest = async (lastRef) => {
let votedialog1 = get("transactions.votedialog1")
let votedialog2 = get("transactions.votedialog2")
let feeDialog = get("walletpage.wchange12")
let myTxnrequest = await parentEpml.request('transaction', {
type: 9,
nonce: this.selectedAddress.nonce,
params: {
fee: voteFeeInput,
voterAddress: this.selectedAddress.address,
rPollName: pollName,
rOptionIndex: optionIndex,
lastReference: lastRef,
votedialog1: votedialog1,
votedialog2: votedialog2,
feeDialog
},
apiVersion: 2
})
return myTxnrequest
}
const getTxnRequestResponse = (txnResponse) => {
if (txnResponse.success === false && txnResponse.message) {
throw new Error(txnResponse.message)
} else if (txnResponse.success === true && !txnResponse.data.error) {
return txnResponse.data
} else if (txnResponse.data && txnResponse.data.message) {
throw new Error(txnResponse.data.message)
} else {
throw new Error('Server error. Could not perform action.')
}
}
const voteRes = await validateReceiver()
return voteRes
}
async _createPoll(pollName, pollDescription, options, pollOwnerAddress) {
const voteFeeInput = await this.unitCreatePollFee()
const getLastRef = async () => {
let myRef = await parentEpml.request('apiCall', {
type: 'api',
url: `/addresses/lastreference/${this.selectedAddress.address}`
})
return myRef
};
const validateReceiver = async () => {
let lastRef = await getLastRef();
let myTransaction = await makeTransactionRequest(lastRef)
const res = getTxnRequestResponse(myTransaction)
return res
}
const makeTransactionRequest = async (lastRef) => {
let votedialog3 = get("transactions.votedialog3")
let votedialog4 = get("transactions.votedialog4")
let votedialog5 = get("transactions.votedialog5")
let votedialog6 = get("transactions.votedialog6")
let feeDialog = get("walletpage.wchange12")
let myTxnrequest = await parentEpml.request('transaction', {
type: 8,
nonce: this.selectedAddress.nonce,
params: {
fee: voteFeeInput,
ownerAddress: pollOwnerAddress,
rPollName: pollName,
rPollDesc: pollDescription,
rOptions: options,
lastReference: lastRef,
votedialog3: votedialog3,
votedialog4: votedialog4,
votedialog5: votedialog5,
votedialog6: votedialog6,
feeDialog
},
apiVersion: 2
})
return myTxnrequest
}
const getTxnRequestResponse = (txnResponse) => {
if (txnResponse.success === false && txnResponse.message) {
throw new Error(txnResponse.message)
} else if (txnResponse.success === true && !txnResponse.data.error) {
return txnResponse.data
} else if (txnResponse.data && txnResponse.data.message) {
throw new Error(txnResponse.data.message)
} else {
throw new Error('Server error. Could not perform action.')
}
}
const voteRes = await validateReceiver()
return voteRes
}
firstUpdated() {
this.changeTheme();
this.changeLanguage();
@ -1324,6 +1467,103 @@ class WebBrowser extends LitElement {
// If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}`
break;
}
case actions.VOTE_ON_POLL: {
const requiredFields = ['pollName', 'optionIndex'];
const missingFields = [];
requiredFields.forEach((field) => {
if (!data[field]) {
missingFields.push(field);
}
});
if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(', ');
const errorMsg = `Missing fields: ${missingFieldsString}`
let data = {};
data['error'] = errorMsg;
response = JSON.stringify(data);
break
}
const pollName = data.pollName;
const optionIndex = data.optionIndex;
let pollInfo = null
try {
pollInfo = await parentEpml.request("apiCall", {
type: "api",
url: `/polls/${pollName}`,
});
} catch (error) {
const errorMsg = (error && error.message) || 'Poll not found';
let obj = {};
obj['error'] = errorMsg;
response = JSON.stringify(obj);
break
}
if (!pollInfo || pollInfo.error) {
const errorMsg = (pollInfo && pollInfo.message) || 'Poll not found';
let obj = {};
obj['error'] = errorMsg;
response = JSON.stringify(obj);
break
}
try {
this.loader.show();
const resVoteOnPoll = await this._voteOnPoll(pollName, optionIndex)
response = JSON.stringify(resVoteOnPoll);
} catch (error) {
const obj = {};
const errorMsg = error.message || 'Failed to vote on the poll.';
obj['error'] = errorMsg;
response = JSON.stringify(obj);
} finally {
this.loader.hide();
}
break;
}
case actions.CREATE_POLL: {
const requiredFields = ['pollName', 'pollDescription', 'pollOptions', 'pollOwnerAddress'];
const missingFields = [];
requiredFields.forEach((field) => {
if (!data[field]) {
missingFields.push(field);
}
});
if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(', ');
const errorMsg = `Missing fields: ${missingFieldsString}`
let data = {};
data['error'] = errorMsg;
response = JSON.stringify(data);
break
}
const pollName = data.pollName;
const pollDescription = data.pollDescription
const pollOptions = data.pollOptions
const pollOwnerAddress = data.pollOwnerAddress
try {
this.loader.show();
const resCreatePoll = await this._createPoll(pollName, pollDescription, pollOptions, pollOwnerAddress)
response = JSON.stringify(resCreatePoll);
} catch (error) {
const obj = {};
const errorMsg = error.message || 'Failed to created poll.';
obj['error'] = errorMsg;
response = JSON.stringify(obj);
} finally {
this.loader.hide();
}
break;
}
case actions.OPEN_NEW_TAB: {
if(!data.qortalLink){
const obj = {};

Loading…
Cancel
Save