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")}
+
+
+ this.readFile(e.target.files[0])}">
+ `
+ }
+
+ 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`
+
+
+ `
+ : ""
+ }
+
+
+
+
+
+
+
+ ${this.sponsorships.length === 0 ? html`
+
+
${translate("sponsorshipspage.schange9")}
+
+ ` : ''}
+ ${this.sponsorships.length > 0 ?
+ html`
+
+
${translate("sponsorshipspage.schange1")}
+
+
+
+
+ ${this.sponsorships.map(
+ (sponsorship) => html`
+
+ -
+
+ ${sponsorship.address}
+
+ -
+
+ ${+sponsorship.blocksMinted +
+ +sponsorship.blocksMintedAdjustment}
+
+
+ -
+
+
+
+ -
+
+ this.removeRewardShare(
+ sponsorship
+ )}
+ >create${translate(
+ "rewardsharepage.rchange17"
+ )}
+
+
+
+ `
+ )}
+
+
+
+
+ ${translate("sponsorshipspage.schange3")} =
+
+ ${this.sponsorships.length}
+
+
+
+
+ ${translate("sponsorshipspage.schange4")} =
+
+ ${this.nextSponsorshipEnding
+ ?.blocksRemaining}
+ ${translate("mintingpage.mchange26")}
+
+
+
+
+ `
+ : ''}
+
${this.createSponsorshipMessage}
+
+
+
+
+
+
+
+
${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)