diff --git a/qortal-ui-core/src/components/sidenav-menu.js b/qortal-ui-core/src/components/sidenav-menu.js index b5bbd39c..0d8e207b 100644 --- a/qortal-ui-core/src/components/sidenav-menu.js +++ b/qortal-ui-core/src/components/sidenav-menu.js @@ -17,7 +17,7 @@ class SidenavMenu extends connect(store)(LitElement) { urls: { type: Object }, nodeType: { type: String, reflect: true }, theme: { type: String, reflect: true }, - addressInfo: { type: Object }, + addressInfo: { type: Object } }; } @@ -102,6 +102,7 @@ class SidenavMenu extends connect(store)(LitElement) { renderNodeTypeMenu() { const addressInfo = this.addressInfo; const isMinter = addressInfo?.error !== 124 && +addressInfo?.level > 0; + const isSponsor = +addressInfo?.level >= 5 if (this.nodeType === 'lite') { return html` @@ -151,7 +152,16 @@ class SidenavMenu extends connect(store)(LitElement) { href="/app/become-minter" > - `} + ` + } + ${isSponsor ? html` + + + + ` : ''} + + + ${this.renderNodeManagement()} - - - `; } } diff --git a/qortal-ui-core/src/functional-components/side-menu-item-style.js b/qortal-ui-core/src/functional-components/side-menu-item-style.js index a90c2d6e..4f956bc9 100644 --- a/qortal-ui-core/src/functional-components/side-menu-item-style.js +++ b/qortal-ui-core/src/functional-components/side-menu-item-style.js @@ -45,6 +45,7 @@ export const sideMenuItemStyle = css` overflow: hidden; text-decoration: none; border-bottom: 1px solid var(--item-border-color); + text-transform: uppercase; } #itemLink:hover { diff --git a/qortal-ui-plugins/build-config.js b/qortal-ui-plugins/build-config.js index 649d615a..2949988b 100644 --- a/qortal-ui-plugins/build-config.js +++ b/qortal-ui-plugins/build-config.js @@ -125,6 +125,10 @@ const generateForPlugins = () => { in: 'plugins/core/become-minter/become-minter.src.js', out: 'plugins/core/become-minter/become-minter.js', }, + { + in: 'plugins/core/sponsorship-list/sponsorship-list.src.js', + out: 'plugins/core/sponsorship-list/sponsorship-list.js', + }, { in: 'plugins/core/puzzles/puzzles.src.js', out: 'plugins/core/puzzles/puzzles.js', diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index 42ba39b1..9ca4301a 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -50,6 +50,7 @@ "@vaadin/grid": "23.1.5", "@vaadin/icons": "23.1.5", "epml": "0.3.3", + "file-saver": "2.0.5", "html-escaper": "3.0.3", "lit": "2.3.0", "lit-translate": "2.0.1", diff --git a/qortal-ui-plugins/plugins/core/components/QortalFileSaver.js b/qortal-ui-plugins/plugins/core/components/QortalFileSaver.js new file mode 100644 index 00000000..1074fff5 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/QortalFileSaver.js @@ -0,0 +1,162 @@ +function saveFile({ data, debug, filename }) { + if (debug) console.log("[qortal-file-saver] starting with ", { data, debug, filename }); + if (!data) throw new Error("[qortal-file-saver] You must pass in data"); + if (!filename) { + if (typeof window !== "undefined" && typeof File !== undefined && data instanceof File) { + filename = data.name; + } + throw new Error("[qortal-file-saver] You must pass in filename"); + } + + const constructorName = (typeof data === "object" && typeof data.constructor === "function" && data.constructor.name) || null; + if (debug) console.log("constructorName:", constructorName); + + const ext = filename.substr(filename.lastIndexOf(".")).toLowerCase(); + + const A = ({ href, download }) => { + const a = document.createElement("a"); + a.href = href; + a.download = download; + return a; + }; + + function convertImageToCanvas({ debug, img }) { + if (debug) console.log("[qortal-file-saver] starting convertImageToCanvas"); + const height = img.height; + const width = img.width; + const canvas = document.createElement("canvas"); + canvas.height = height; + canvas.width = width; + const context = canvas.getContext("2d"); + context.drawImage(img, 0, 0, width, height); + return canvas; + } + + function saveHTML({ data, debug, filename }) { + if (typeof data === "object" && "outerHTML" in data) { + if (debug) console.log("[qortal-file-saver] data appears to be an HTML element, so grabbing it's outer HTML"); + data = data.outerHTML; + } + const url = "data:text/html," + encodeURIComponent(data); + saveDataOrBlobURL({ url, debug, filename }); + } + + function saveCanvas({ data, debug, filename, imageType }) { + const url = data.toDataURL("image/" + imageType); + saveDataOrBlobURL({ url, debug, filename }); + } + + function saveDataOrBlobURL({ url, debug, filename }) { + A({ href: url, download: filename }).click(); + } + + function saveImageAsJPG({ data: img, debug, filename }) { + if (debug) console.log("starting saveImageAsJPG"); + const canvas = convertImageToCanvas({ debug, img }); + saveCanvasAsJPG({ data: canvas, debug, filename }); + } + + function saveImageAsPNG({ data: img, debug, filename }) { + if (debug) console.log("starting saveImageAsPNG"); + const canvas = convertImageToCanvas({ debug, img }); + saveCanvasAsPNG({ data: canvas, debug, filename }); + } + + function saveImageAsWebP({ data: img, debug, filename }) { + if (debug) console.log("starting saveImageAsWebP"); + const canvas = convertImageToCanvas({ debug, img }); + saveCanvasAsWebP({ data: canvas, debug, filename }); + } + + function saveCanvasAsJPG({ data, debug, filename }) { + saveCanvas({ data, debug, filename, imageType: "jpeg" }); + } + + function saveCanvasAsPNG({ data, debug, filename }) { + saveCanvas({ data, debug, filename, imageType: "png" }); + } + + function saveCanvasAsWebP({ data, debug, filename }) { + saveCanvas({ data, debug, filename, imageType: "webp" }); + } + + function saveDSV({ data, debug, delimiter, filename, mediatype }) { + if (!Array.isArray(data)) throw new Error("[qortal-saver] data must be an array to save as a CSV"); + if (!delimiter) throw new Error("[qortal-saver] delimiter must be set"); + if (!mediatype) throw new Error("[qortal-saver] mediatype must be set"); + let output = "data:" + mediatype + ";charset=utf-8,"; + const columns = Array.from(new Set(data.map(Object.keys).flat())).sort(); + const types = new Set(data.map(it => (Array.isArray(it) ? "array" : typeof it))); + const includeHeader = types.has("object"); + if (debug) console.log("includeHeader:", includeHeader); + if (includeHeader) output += columns.map(c => '"' + c.replace(/,/g, "\\,") + '"') + "\n"; + for (let i = 0; i < data.length; i++) { + const row = data[i]; + if (i !== 0) output += "\n"; + output += columns.map(col => '"' + row[col].toString().replace(/,/g, "\\,") + '"'); + } + const url = encodeURI(output); + saveDataOrBlobURL({ url, debug, filename }); + } + + function saveCSV({ data, debug, filename }) { + saveDSV({ data, debug, delimiter: ",", filename, mediatype: "text/csv" }); + } + + function saveTSV({ data, debug, filename }) { + saveDSV({ data, debug, delimiter: "\t", filename, mediatype: "text/tab-separated-values" }); + } + + function saveJSON({ data, debug, filename }) { + const url = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data, undefined, 2)); + saveDataOrBlobURL({ url, debug, filename }); + } + + function saveText({ data, debug, filename }) { + const url = "data:text/plain;charset=utf-8," + encodeURIComponent(data); + saveDataOrBlobURL({ url, debug, filename }); + } + + function saveBlob({ data, debug, filename }) { + const url = URL.createObjectURL(data); + if (debug) console.log("[qortal-file-saver.saveBlob] url:", url); + saveDataOrBlobURL({ url, debug, filename }); + URL.revokeObjectURL(url); + } + + if (ext === ".csv") { + saveCSV({ data, debug, filename }); + } else if (ext === ".tsv") { + saveTSV({ data, debug, filename }); + } else if (ext === ".html") { + saveHTML({ data, debug, filename }); + } else if (ext === ".json" || ext === ".geojson" || ext === ".topojson") { + saveJSON({ data, debug, filename }); + } else if (ext === ".txt" || ext === ".js" || ext === ".py") { + saveText({ data, debug, filename }); + } else if (constructorName === "HTMLCanvasElement" && ext === ".png") { + saveCanvasAsPNG({ data, debug, filename }); + } else if (constructorName === "HTMLCanvasElement" && ext === ".jpg") { + saveCanvasAsJPG({ data, debug, filename }); + } else if (constructorName === "HTMLCanvasElement" && ext === ".webp") { + saveCanvasAsWebP({ data, debug, filename }); + } else if (constructorName === "HTMLImageElement" && ext === ".jpg") { + saveImageAsJPG({ data, debug, filename }); + } else if (constructorName === "HTMLImageElement" && ext === ".png") { + saveImageAsPNG({ data, debug, filename }); + } else if (constructorName === "HTMLImageElement" && ext === ".webp") { + saveImageAsWebP({ data, debug, filename }); + } else if (constructorName === "Blob") { + saveBlob({ data, debug, filename }); + } else { + throw new Error('[qortal-file-saver] unrecognized extension "' + ext + '"'); + } +} + +if (typeof define === "function" && define.amd) + define(function () { + return saveFile; + }); +if (typeof module === "object") module.exports = saveFile; +if (typeof window === "object") window.saveFile = saveFile; +if (typeof self === "object") self.saveFile = saveFile; \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/components/frag-file-input.js b/qortal-ui-plugins/plugins/core/components/frag-file-input.js new file mode 100644 index 00000000..0816f78d --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/frag-file-input.js @@ -0,0 +1,115 @@ +import { LitElement, html, css } from 'lit' + +import '@material/mwc-button' +import '@material/mwc-icon' + +import { translate, translateUnsafeHTML } from 'lit-translate' + +class FragFileInput extends LitElement { + static get properties () { + return { + accept: { type: String }, + readAs: { type: String } + } + } + + static get styles () { + return css` + #drop-area { + border: 2px dashed #ccc; + font-family: "Roboto", sans-serif; + padding: 20px; + } + + #trigger:hover { + cursor: pointer; + } + + #drop-area.highlight { + border-color: var(--mdc-theme-primary, #000); + } + + p { + margin-top: 0; + } + + form { + margin-bottom: 10px; + } + + #fileInput { + display: none; + } + ` + } + + constructor () { + super() + this.readAs = this.readAs || 'Text' + } + + render () { + return html` +
+ +
+ this.shadowRoot.getElementById('fileInput').click()} style="dispay:inline;"> + cloud_upload  ${translate("fragfile.selectfile")} +
+ ${translate("fragfile.dragfile")} +
+
+ + ` + } + + readFile (file) { + const fr = new FileReader() + fr.onload = () => { + this.dispatchEvent(new CustomEvent('file-read-success', { + detail: { result: fr.result }, + bubbles: true, + composed: true + })) + } + fr['readAs' + this.readAs](file) + } + + firstUpdated () { + this._dropArea = this.shadowRoot.getElementById('drop-area') + + const preventDefaults = e => { + e.preventDefault() + e.stopPropagation() + } + + ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + this._dropArea.addEventListener(eventName, preventDefaults, false) + }) + + const highlight = e => { + this._dropArea.classList.add('highlight') + } + + const unhighlight = e => { + this._dropArea.classList.remove('highlight') + } + + ;['dragenter', 'dragover'].forEach(eventName => { + this._dropArea.addEventListener(eventName, highlight, false) + }) + + ;['dragleave', 'drop'].forEach(eventName => { + this._dropArea.addEventListener(eventName, unhighlight, false) + }) + + this._dropArea.addEventListener('drop', e => { + const dt = e.dataTransfer + const file = dt.files[0] + + this.readFile(file) + }, false) + } +} + +window.customElements.define('frag-file-input', FragFileInput) diff --git a/qortal-ui-plugins/plugins/core/main.src.js b/qortal-ui-plugins/plugins/core/main.src.js index b4efcb9f..7c554a84 100644 --- a/qortal-ui-plugins/plugins/core/main.src.js +++ b/qortal-ui-plugins/plugins/core/main.src.js @@ -25,6 +25,15 @@ parentEpml.ready().then(() => { menus: [], parent: false, }, + { + url: 'sponsorship-list', + domain: 'core', + page: 'sponsorship-list/index.html', + title: 'Become a Minter', + icon: 'vaadin:info-circle', + menus: [], + parent: false, + }, { url: 'wallet', domain: 'core', diff --git a/qortal-ui-plugins/plugins/core/sponsorship-list/index.html b/qortal-ui-plugins/plugins/core/sponsorship-list/index.html new file mode 100644 index 00000000..b4e15984 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/sponsorship-list/index.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list-css.src.js b/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list-css.src.js new file mode 100644 index 00000000..1851c584 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list-css.src.js @@ -0,0 +1,359 @@ +import { css } from "lit" + +export const pageStyles = css` + * { + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + box-sizing: border-box; + } + + .header-title { + font-size: 40px; + color: var(--black); + font-weight: 400; + text-align: center; + } + .divider { + color: #eee; + border-radius: 80%; + margin-bottom: 2rem; + } + .fullWidth { + width: 100%; + } + .page-container { + display: flex; + align-items: center; + flex-direction: column; + margin-bottom: 75px; + } + .inner-container { + display: flex; + align-items: center; + flex-direction: column; + width: 95%; + max-width: 1100px; + } + + + + .message { + color: var(--gray); + } + + + + .form-wrapper { + display: flex; + align-items: center; + width: 100%; + max-width: 700px; + height: 50px; + flex-wrap: wrap; + } + + .sponsor-minter-text { + color: var(--black); + font-weight: bold; + margin-right: 15px; + font-size: 18px; + } + + .row { + display: flex; + width: 100%; + } + .column { + display: flex; + flex-direction: column; + width: 100%; + } + + .column-center { + align-items: center; + } + .no-margin { + margin: 0; + } + .no-wrap { + flex-wrap: nowrap !important; + } + + .row-center { + justify-content: center; + flex-wrap: wrap; + } + .form-item { + display: flex; + height: 100%; + } + + .form-item--button { + flex-grow: 0; + } + + .form-item--input { + flex-grow: 1; + margin-right: 25px; + min-width: 275px; + } + + + + + .gap { + gap: 10px; + } + + .title { + font-weight: 600; + font-size: 20px; + line-height: 28px; + opacity: 0.66; + color: var(--switchborder); + } + + .address { + overflow-wrap: anywhere; + color: var(--black); + } + + h4 { + font-weight: 600; + font-size: 20px; + line-height: 28px; + color: var(--black); + } + mwc-textfield { + width: 100%; + } + vaadin-button { + height: 100%; + margin: 0; + cursor: pointer; + outline: 1px var(--black) solid; + min-width: 80px; + } + .loader, + .loader:after { + border-radius: 50%; + width: 10em; + height: 10em; + } + .loadingContainer { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10; + } + + .backdrop { + height: 100vh; + width: 100vw; + opacity: 0.6; + background-color: var(--border); + z-index: 9; + position: fixed; + } + + .loading, + .loading:after { + border-radius: 50%; + width: 5em; + height: 5em; + } + + .loading { + margin: 10px auto; + border-width: 0.6em; + border-style: solid; + border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) + rgba(3, 169, 244, 0.2) rgb(3, 169, 244); + font-size: 10px; + position: relative; + text-indent: -9999em; + transform: translateZ(0px); + animation: 1.1s linear 0s infinite normal none running loadingAnimation; + } + + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + .tableGrid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax( + 0, + 1fr + ) minmax(0, 1fr); + align-items: center; + gap: 5px; + width: 100%; + margin-bottom: 15px; + + padding: 5px; + + } + + + .grid-item { + text-align: center; + color: var(--black); + word-break: break-all; + overflow: hidden; + } + + .text { + color: var(--black) + } + .text--bold { + font-weight: bold; + } + + .summary-box { + + display: flex; + margin-top: 25px; + width: 100%; + flex-wrap: wrap; + } + + .summary-box p:first-child { + margin-right: 30px; + } + + .text--normal { + font-weight: normal; + } + + .grid-item p { + text-decoration: underline; + } + + ul { + list-style-type: none; + margin: 0; + padding: 0; + } + .red { + --mdc-theme-primary: #f44336; + border-radius: 2px; + + } + .btn--sponsorshipfinished { + background-color: var(--menuactive); + transition: all .2s; + animation: onOff 2s infinite; + --mdc-theme-primary: var(--black); + } + + + + .dialog-container { + width: 300px; + min-height: 300px; + max-height: 75vh; + padding: 5px; + display: flex; + align-items: flex-start; + flex-direction: column; + + + } + + .dialog-paragraph { + word-break: break-all; + color: var(--black) + } + + + + .dialog-header h1 { + font-size: 18px; + } + + @keyframes onOff { + from {opacity: 1} + to {opacity: .5} +} + .grid-item-text { + display: none; + } + + .sub-title { + margin-bottom: 10px; + } + + .sub-title p { + font-size: 18px; + color: var(--black); + } + + @media (max-width: 610px) { + .sponsor-minter-wrapper { + width: 100%; + margin-bottom: 10px; + } + + .form-item--input { + flex-grow: 1; + margin-right: 25px; + min-width: unset; + } + } + + @media (max-width: 710px) { + .table-header { + display: none; + } + .grid-item-text { + display: inline; + color: var(--black); + text-decoration: none; + margin: 0px; + margin-right: 10px; + } + + .grid-item { + text-align: start; + align-items: center; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + } + + .grid-item p { + text-decoration: none; + } + + .tableGrid { + grid-template-columns: minmax(0, 1fr); + border-radius: 5px; + border: 1px solid var(--black); + padding: 10px; + margin-bottom: 20px; + } + + mwc-button { + grid-column: 1 / -1; + } + } +` diff --git a/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list.src.js b/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list.src.js new file mode 100644 index 00000000..3a3696d0 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/sponsorship-list/sponsorship-list.src.js @@ -0,0 +1,607 @@ +import { LitElement, html } from "lit" +import { Epml } from "../../../epml.js" +import "../components/ButtonIconCopy.js" +import { use, get, translate, registerTranslateConfig } from "lit-translate" +import { blocksNeed } from "../../utils/blocks-needed.js" +import "../components/ButtonIconCopy.js" + +registerTranslateConfig({ + loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json()), +}) + +import "@polymer/paper-spinner/paper-spinner-lite.js" +import "@material/mwc-button" +import "@material/mwc-textfield" +import "@vaadin/button" +import "@material/mwc-button" +import "@polymer/paper-spinner/paper-spinner-lite.js" +import '@material/mwc-dialog' + +import { pageStyles } from "./sponsorship-list-css.src.js" + +const parentEpml = new Epml({ type: "WINDOW", source: window.parent }) + +class SponsorshipList extends LitElement { + static get properties() { + return { + theme: { type: String, reflect: true }, + nodeInfo: { type: Object }, + isPageLoading: { type: Boolean }, + addressInfo: { type: Object }, + rewardSharePublicKey: { type: String }, + mintingAccountData: { type: Array }, + sponsorships: { type: Array }, + removeRewardShareLoading: { type: Array }, + createSponsorshipMessage: { type: String }, + isLoadingCreateSponsorship: { type: Array }, + publicKeyValue: { type: String }, + error: { type: Boolean }, + isOpenModal: {type: Boolean} + } + } + + static styles = [pageStyles] + + constructor() { + super() + this.theme = localStorage.getItem("qortalTheme") + ? localStorage.getItem("qortalTheme") + : "light" + this.isPageLoading = true + this.nodeInfo = {} + this.addressInfo = {} + this.rewardSharePublicKey = "" + this.mintingAccountData = null + this.sponsorships = [] + this.removeRewardShareLoading = false + this.error = false + this.createSponsorshipMessage = "" + this.isLoadingCreateSponsorship = false + this.publicKeyValue = "" + this.isOpenModal = false + } + + inputHandler(e) { + this.publicKeyValue = e.target.value + } + + changeLanguage() { + const checkLanguage = localStorage.getItem("qortalLanguage") + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem("qortalLanguage", "us") + use("us") + } else { + use(checkLanguage) + } + } + + _handleStorage() { + const checkLanguage = localStorage.getItem("qortalLanguage") + const checkTheme = localStorage.getItem("qortalTheme") + + use(checkLanguage) + + if (checkTheme === "dark") { + this.theme = "dark" + } else { + this.theme = "light" + } + document.querySelector("html").setAttribute("theme", this.theme) + } + + connectedCallback() { + super.connectedCallback() + window.addEventListener("storage", this._handleStorage) + } + + disconnectedCallback() { + window.removeEventListener("storage", this._handleStorage) + super.disconnectedCallback() + } + + async getNodeInfo() { + const nodeInfo = await parentEpml.request("apiCall", { + url: `/admin/status`, + }) + + return nodeInfo + } + + async atMount() { + this.changeLanguage() + + this.addressInfo = + window.parent.reduxStore.getState().app.accountInfo.addressInfo + this.isPageLoading = true + try { + + const address = + window.parent.reduxStore.getState().app?.selectedAddress + ?.address + let rewardShares = await this.getRewardShareRelationship( + address + ) + + rewardShares = rewardShares.filter((rs) => rs.recipient !== address) + + const getAccountInfo = rewardShares.map(async (rs) => { + const addressInfo = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/${rs.recipient}`, + }) + + let blocksRemaining = this._levelUpBlocks(addressInfo) + blocksRemaining = +blocksRemaining > 0 ? +blocksRemaining : 0 + return { + ...addressInfo, + ...rs, + + blocksRemaining: blocksRemaining, + } + }) + const accountInfoValues = await Promise.all(getAccountInfo) + + this.sponsorships = accountInfoValues + this.nextSponsorshipEnding = accountInfoValues + .filter((sponsorship) => sponsorship.blocksRemaining !== 0) + .sort((a, b) => a.blocksRemaining - b.blocksRemaining)[0] + this.isPageLoading = false + + const openModal = accountInfoValues.find(s=> s.blocksRemaining <= 0) + if(openModal){ + this.shadowRoot.querySelector('#showDialog').show() + } + } catch (error) { + + + this.isPageLoading = false + } + } + + async firstUpdated() { + await this.atMount() + } + + async getRewardShareRelationship(recipientAddress) { + const myRewardShareArray = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/rewardshares?minters=${recipientAddress}`, + }) + + return myRewardShareArray + } + + _levelUpBlocks(accountInfo) { + let countBlocksString = ( + blocksNeed(0) - + (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment) + ).toString() + return countBlocksString + } + + async removeRewardShare(rewardShareObject) { + + const selectedAddress = + window.parent.reduxStore.getState().app?.selectedAddress + + const myPercentageShare = -1 + + // Check for valid... + this.removeRewardShareLoading = true + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/lastreference/${selectedAddress?.address}`, + }) + return myRef + } + + // Remove Reward Share + const removeReceiver = async () => { + let lastRef = await getLastRef() + + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let mylastRef = lastRef + let rewarddialog5 = get("transactions.rewarddialog5") + let rewarddialog6 = get("transactions.rewarddialog6") + let myTxnrequest = await parentEpml.request("transaction", { + type: 381, + nonce: selectedAddress?.nonce, + params: { + rewardShareKeyPairPublicKey: + rewardShareObject.rewardSharePublicKey, + recipient: rewardShareObject.recipient, + percentageShare: myPercentageShare, + lastReference: mylastRef, + rewarddialog5: rewarddialog5, + rewarddialog6: rewarddialog6, + }, + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.removeRewardShareLoading = false + parentEpml.request("showSnackBar", txnResponse.message) + throw new Error(txnResponse) + } else if ( + txnResponse.success === true && + !txnResponse.data.error + ) { + let err7tring = get("rewardsharepage.rchange22") + this.removeRewardShareLoading = false + parentEpml.request("showSnackBar", `${err7tring}`) + this.sponsorships = this.sponsorships.filter((s)=> s.address !== rewardShareObject.address) + } else { + this.removeRewardShareLoading = false + parentEpml.request("showSnackBar", txnResponse.data.message) + throw new Error(txnResponse) + } + } + removeReceiver() + } + + async createRewardShare(e) { + this.error = false + this.createSponsorshipMessage = "" + const recipientPublicKey = this.publicKeyValue + const percentageShare = 0 + const selectedAddress = + window.parent.reduxStore.getState().app?.selectedAddress + // Check for valid... + this.isLoadingCreateSponsorship = true + + let recipientAddress = + window.parent.base58PublicKeyToAddress(recipientPublicKey) + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/lastreference/${selectedAddress.address}`, + }) + return myRef + } + + // Get Account Details + const getAccountDetails = async () => { + let myAccountDetails = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/${selectedAddress.address}`, + }) + return myAccountDetails + } + + // Get Reward Relationship if it already exists + const getRewardShareRelationship = async (minterAddr) => { + let isRewardShareExisting = false + let myRewardShareArray = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/rewardshares?minters=${minterAddr}&recipients=${recipientAddress}`, + }) + isRewardShareExisting = + myRewardShareArray.length !== 0 ? true : false + return isRewardShareExisting + } + + // Validate Reward Share by Level + const validateReceiver = async () => { + let accountDetails = await getAccountDetails() + let lastRef = await getLastRef() + let isExisting = await getRewardShareRelationship( + selectedAddress.address + ) + + // Check for creating self share at different levels (also adding check for flags...) + if (accountDetails.flags === 1) { + this.error = false + this.createSponsorshipMessage = "" + let myTransaction = await makeTransactionRequest(lastRef) + if (isExisting === true) { + this.error = true + this.createSponsorshipMessage = `Cannot Create Multiple Reward Shares!` + } else { + // Send the transaction for confirmation by the user + this.error = false + this.createSponsorshipMessage = "" + getTxnRequestResponse(myTransaction) + } + } else if (accountDetails.address === recipientAddress) { + if (accountDetails.level >= 1 && accountDetails.level <= 4) { + this.error = false + this.createSponsorshipMessage = "" + let myTransaction = await makeTransactionRequest(lastRef) + if (isExisting === true) { + let err1string = get("rewardsharepage.rchange18") + this.error = true + this.createSponsorshipMessage = `${err1string}` + } else { + // Send the transaction for confirmation by the user + this.error = false + this.createSponsorshipMessage = "" + getTxnRequestResponse(myTransaction) + } + } else if (accountDetails.level >= 5) { + this.error = false + this.createSponsorshipMessage = "" + let myTransaction = await makeTransactionRequest(lastRef) + if (isExisting === true) { + let err2string = get("rewardsharepage.rchange19") + this.error = true + this.createSponsorshipMessage = `${err2string}` + } else { + // Send the transaction for confirmation by the user + this.error = false + this.createSponsorshipMessage = "" + getTxnRequestResponse(myTransaction) + } + } else { + let err3string = get("rewardsharepage.rchange20") + this.error = true + this.createSponsorshipMessage = `${err3string} ${accountDetails.level}` + } + } else { + //Check for creating reward shares + if (accountDetails.level >= 5) { + this.error = false + this.createSponsorshipMessage = "" + let myTransaction = await makeTransactionRequest(lastRef) + if (isExisting === true) { + let err4string = get("rewardsharepage.rchange18") + this.error = true + this.createSponsorshipMessage = `${err4string}` + } else { + // Send the transaction for confirmation by the user + this.error = false + this.createSponsorshipMessage = "" + getTxnRequestResponse(myTransaction) + } + } else { + this.error = true + let err5string = get("rewardsharepage.rchange20") + this.createSponsorshipMessage = `${err5string} ${accountDetails.level}` + } + } + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let mylastRef = lastRef + let rewarddialog1 = get("transactions.rewarddialog1") + let rewarddialog2 = get("transactions.rewarddialog2") + let rewarddialog3 = get("transactions.rewarddialog3") + let rewarddialog4 = get("transactions.rewarddialog4") + let myTxnrequest = await parentEpml.request("transaction", { + type: 38, + nonce: selectedAddress.nonce, + params: { + recipientPublicKey, + percentageShare, + lastReference: mylastRef, + rewarddialog1: rewarddialog1, + rewarddialog2: rewarddialog2, + rewarddialog3: rewarddialog3, + rewarddialog4: rewarddialog4, + }, + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.error = true + this.createSponsorshipMessage = txnResponse.message + throw new Error(txnResponse) + } else if ( + txnResponse.success === true && + !txnResponse.data.error + ) { + let err6string = get("rewardsharepage.rchange21") + this.createSponsorshipMessage = err6string + this.error = false + } else { + this.error = true + this.createSponsorshipMessage = txnResponse.data.message + throw new Error(txnResponse) + } + } + validateReceiver() + this.isLoadingCreateSponsorship = false + } + + + render() { + + return html` + ${ + this.isPageLoading + ? html` +
+
+
+
+ ` + : "" + } + +
+

+ ${translate("mintingpage.mchange35")} +

+
+
+
+
+ ${this.sponsorships.length === 0 ? html` +
+

${translate("sponsorshipspage.schange9")}

+
+ ` : ''} + ${this.sponsorships.length > 0 ? + html` +
+

${translate("sponsorshipspage.schange1")}

+
+
+
+

${translate("sponsorshipspage.schange2")}

+
+
+

${translate("walletprofile.blocksminted")}

+
+ +
+

${translate("becomeMinterPage.bchange17")}

+
+
+ +
+
+ + + ${this.sponsorships.map( + (sponsorship) => html` + + ` + )} + + +
+

+ ${translate("sponsorshipspage.schange3")} = + + ${this.sponsorships.length} + + +

+

+ ${translate("sponsorshipspage.schange4")} = + + ${this.nextSponsorshipEnding + ?.blocksRemaining} + ${translate("mintingpage.mchange26")} + + +

+
+ ` + : ''} +

${this.createSponsorshipMessage}

+
+ +
+ + +
+
+ + ${ + this.isLoadingCreateSponsorship === false + ? html`${translate( + "puzzlepage.pchange15" + )}` + : html`` + } + +
+
+
+ + +
+

${translate("sponsorshipspage.schange6")}

+
+
+
+ +

${this.sponsorships.filter(s=> s.blocksRemaining <= 0).length} ${translate("sponsorshipspage.schange7")}!

+ +

${translate("sponsorshipspage.schange8")}

+ ${this.sponsorships.filter(s=> s.blocksRemaining <= 0).map((ms)=> html` +

${ms.address}

+ `)} +
+ + + ${translate("general.close")} + + +
+
+ ` + } +} + +window.customElements.define("sponsorship-list", SponsorshipList)