From 3241775af439308a77a822e3caf98dcd17edbc83 Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Wed, 24 Jul 2024 02:03:50 -0600 Subject: [PATCH 01/12] build: :package: updates to support crowdnode --- package-lock.json | 177 ++++++++++++++++++++++++++++++++++++++++------ package.json | 6 +- 2 files changed, 159 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 096adb4..6f0e9ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,11 @@ "dependencies": { "@dashincubator/base58check": "^1.4.1", "@dashincubator/ripemd160": "^3.0.0", - "@dashincubator/secp256k1": "^1.7.1-5", + "@dashincubator/secp256k1": "dashhive/secp256k1.js#sync-with-upstream", "@zxing/library": "^0.21.2", + "crowdnode": "dashhive/crowdnode.js#ref-dashtx", "crypticstorage": "^0.0.2", - "dashhd": "^3.3.3", + "dashhd": "dashhive/DashHD.js#ref-secp256k1-2.1.0-compat", "dashkeys": "^1.1.4", "dashphrase": "^1.4.0", "dashsight": "dashhive/DashSight.js#feat/bulk-txs", @@ -33,6 +34,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/@dashincubator/base58check/-/base58check-1.4.1.tgz", "integrity": "sha512-JSAO+viM3pVgM93XSBAUhLvK18BlRIsmYU4eGuyrj1lz3Xa1oplSel94oG0SI5d2qTxdy0NfGT3vEFbiKpfj5Q==", + "license": "SEE LICENSE IN LICENSE", "bin": { "base58check": "bin/base58check.js" } @@ -40,17 +42,25 @@ "node_modules/@dashincubator/ripemd160": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@dashincubator/ripemd160/-/ripemd160-3.0.0.tgz", - "integrity": "sha512-EbdXcceP2mW76NchCKp8UYNbZgWkLuV4Mbi30G82xRED32ljJzXsKaaVdzU0oVo2fVzPRXF1GhSF6Lq9beTVvA==" + "integrity": "sha512-EbdXcceP2mW76NchCKp8UYNbZgWkLuV4Mbi30G82xRED32ljJzXsKaaVdzU0oVo2fVzPRXF1GhSF6Lq9beTVvA==", + "license": "MIT" }, "node_modules/@dashincubator/secp256k1": { - "version": "1.7.1-5", - "resolved": "https://registry.npmjs.org/@dashincubator/secp256k1/-/secp256k1-1.7.1-5.tgz", - "integrity": "sha512-3iA+RDZrJsRFPpWhlYkp3EdoFAlKjdqkNFiRwajMrzcpA/G/IBX0AnC1pwRLkTrM+tUowcyGrkJfT03U4ETZeg==" + "version": "2.1.0", + "resolved": "git+ssh://git@github.com/dashhive/secp256k1.js.git#d587c1a24ca7d5c47f7cb519ca70a54920da3245", + "license": "MIT" + }, + "node_modules/@root/request": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.2.tgz", + "integrity": "sha512-wVaL9yVV9oDR9UNbPZa20qgY+4Ch6YN8JUkaE4el/uuS5dmhD8Lusm/ku8qJVNtmQA56XLzEDCRS6/vfpiHK2A==", + "license": "(MIT OR Apache-2.0)", + "optional": true }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "20.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", + "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -76,17 +86,40 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", "optional": true }, + "node_modules/crowdnode": { + "version": "1.8.0", + "resolved": "git+ssh://git@github.com/dashhive/crowdnode.js.git#2cfe200186a3d3ed7d1fb29edac0657825ddf7d0", + "license": "SEE LICENSE IN LICENSE", + "bin": { + "crowdnode": "bin/crowdnode.js" + }, + "optionalDependencies": { + "@dashincubator/base58check": "^1.4.1", + "@dashincubator/ripemd160": "^3.0.0", + "@dashincubator/secp256k1": "dashhive/secp256k1.js#sync-with-upstream", + "@root/request": "^1.9.2", + "dashhd": "dashhive/DashHD.js#ref-secp256k1-2.1.0-compat", + "dashkeys": "^1.1.4", + "dashsight": "^1.6.1", + "dashtx": "^0.16.0", + "dotenv": "^16.4.5", + "qrcode-svg": "^1.1.0", + "tough-cookie": "^4.1.4", + "ws": "^8.18.0" + } + }, "node_modules/crypticstorage": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypticstorage/-/crypticstorage-0.0.2.tgz", - "integrity": "sha512-PyiKq03ekJU1AwzYVchlm+LaUvDy939S1smmCD0fa60HLKVM0m6zEj4J/MXcm1BLrwdaZk9eRdJcau77nIOPAw==" + "integrity": "sha512-PyiKq03ekJU1AwzYVchlm+LaUvDy939S1smmCD0fa60HLKVM0m6zEj4J/MXcm1BLrwdaZk9eRdJcau77nIOPAw==", + "license": "MIT" }, "node_modules/dashhd": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dashhd/-/dashhd-3.3.3.tgz", - "integrity": "sha512-sbhLV8EtmebnlIdx/d1hcbnxdfka/0rcLx+UO5y44kZdu5tyJ5ftBFbhhIb38vd+T+Xfcwpeo0z+0ZDznRkfaw==", + "resolved": "git+ssh://git@github.com/dashhive/DashHD.js.git#92cf91acf5633ed2ebed3b4f39d0d0f1d830679e", "license": "SEE LICENSE IN LICENSE" }, "node_modules/dashkeys": { @@ -98,11 +131,13 @@ "node_modules/dashphrase": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/dashphrase/-/dashphrase-1.4.0.tgz", - "integrity": "sha512-o+LdiPkiYmg07kXBE+2bbcJzBmeTQVPn1GS2XlQeo8lene+KknAprSyiYi5XtqV/QVgNjvzOV7qBst2MijSPAA==" + "integrity": "sha512-o+LdiPkiYmg07kXBE+2bbcJzBmeTQVPn1GS2XlQeo8lene+KknAprSyiYi5XtqV/QVgNjvzOV7qBst2MijSPAA==", + "license": "SEE LICENSE IN LICENSE" }, "node_modules/dashsight": { "version": "1.6.1", "resolved": "git+ssh://git@github.com/dashhive/DashSight.js.git#11c1a740057ba646f89e8da2d85cb35dc67085f3", + "license": "SEE LICENSE IN LICENSE", "dependencies": { "dashtx": "^0.9.0-3" }, @@ -121,6 +156,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/dashtx/-/dashtx-0.9.0.tgz", "integrity": "sha512-DDbH5vPChUpOrYMOoM+6g/Iy99KqG4nkJ6f8TphnGibzAY7mitjMgtFSc62YzbZdoPGYeSPm8N4jmz+Mbwm7Eg==", + "license": "SEE LICENSE IN LICENSE", "bin": { "dashtx-inspect": "bin/inspect.js" } @@ -156,36 +192,41 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", "optional": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/html5-qrcode": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz", - "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==" + "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==", + "license": "Apache-2.0" }, "node_modules/idb": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", - "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==" + "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==", + "license": "ISC" }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" }, "node_modules/lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "license": "MIT", "dependencies": { "immediate": "~3.0.5" } @@ -194,22 +235,72 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "license": "Apache-2.0", "dependencies": { "lie": "3.1.1" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "license": "MIT", + "optional": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qrcode-svg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz", "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==", + "license": "MIT", "bin": { "qrcode-svg": "bin/qrcode-svg.js" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT", + "optional": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT", + "optional": true + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-custom-error": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -218,7 +309,51 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index e44fb16..e904035 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,11 @@ "dependencies": { "@dashincubator/base58check": "^1.4.1", "@dashincubator/ripemd160": "^3.0.0", - "@dashincubator/secp256k1": "^1.7.1-5", + "@dashincubator/secp256k1": "dashhive/secp256k1.js#sync-with-upstream", "@zxing/library": "^0.21.2", - "crowdnode": "^1.8.0", + "crowdnode": "dashhive/crowdnode.js#ref-dashtx", "crypticstorage": "^0.0.2", - "dashhd": "^3.3.3", + "dashhd": "dashhive/DashHD.js#ref-secp256k1-2.1.0-compat", "dashkeys": "^1.1.4", "dashphrase": "^1.4.0", "dashsight": "dashhive/DashSight.js#feat/bulk-txs", From 34201939d9e547be231d722f4146c14269cf87ea Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:43:20 -0600 Subject: [PATCH 02/12] feat: :lipstick: initial implementation of UI for CrowdNode --- src/components/crowdnode-card.js | 221 +++++++++++++ src/components/dialog.js | 119 ++++--- src/components/modal.js | 543 +++++++++++++++++++++++++++++++ src/components/svg-sprite.js | 4 + src/helpers/constants.js | 12 + src/helpers/lit.js | 18 +- src/helpers/utils.js | 313 +++++++++++++++++- src/imports.js | 8 +- src/index.css | 64 +++- src/main.js | 384 +++++++++++++++++++--- src/rigs/confirm-action.js | 34 +- src/rigs/crowdnode-tx.js | 135 ++++++++ src/rigs/send-or-request.js | 81 +++-- src/rigs/show-error.js | 2 +- src/secp.js | 11 + src/styles/components.css | 46 +++ src/styles/dialog.css | 4 +- src/styles/form.css | 25 +- src/styles/motion.css | 12 +- src/styles/progress.css | 29 +- src/styles/theme.css | 13 +- 21 files changed, 1902 insertions(+), 176 deletions(-) create mode 100644 src/components/crowdnode-card.js create mode 100644 src/components/modal.js create mode 100644 src/rigs/crowdnode-tx.js create mode 100644 src/secp.js diff --git a/src/components/crowdnode-card.js b/src/components/crowdnode-card.js new file mode 100644 index 0000000..d0bceb2 --- /dev/null +++ b/src/components/crowdnode-card.js @@ -0,0 +1,221 @@ +import { + formDataEntries, + createSignal, +} from '../helpers/utils.js' + +import { + lit as html, +} from '../helpers/lit.js' + +export const CrowdNodeCard = (() => { + const initCfg = { + state: {}, + slugs: {}, + events: {}, + elements: {}, + markup: {}, + } + const initialState = { + id: 'Card', + name: 'Card', + withdrawTxt: 'Withdraw', + withdrawAlt: 'Withdraw from Crowdnode', + depositTxt: 'Deposit', + depositAlt: `Deposit to Crowdnode`, + signupTxt: 'Signup', + signupAlt: `Signup for Crowdnode`, + placement: 'center', + rendered: null, + responsive: true, + } + + const cn = function Crowdnode( + config = {} + ) { + config = { + ...initCfg, + ...config, + } + + this.appElement = document.body + + this.api = createSignal({}) + + this.state = { + ...initialState, + ...config.state, + } + + this.slugs = { + form: this.state.name?.toLowerCase().replaceAll(' ', '_'), + ...config.slugs, + } + + this.elements = { + ...config.elements, + } + + this.markup = {} + this.markup.content = () => html` +
+ + + CrowdNode + +
+ +
+ ${!this.api.value?.acceptedToS && html` + Start earning interest by staking your Dash at CrowdNode + `} + ${this.api.value?.acceptedToS && html` +

+ Balance: Ð ${this.api.value?.balance} +

+ `} + ${this.api.value?.acceptedToS && html` +

+ Earned: Ð ${this.api.value?.earned} +

+ `} +
+ + + ` + this.markup = { + ...this.markup, + ...config.markup, + } + + this.events = { + submit: event => { + event.preventDefault() + event.stopPropagation() + + this.elements.form?.removeEventListener('submit', this.events.submit) + + let fde = formDataEntries(event) + + console.log( + `${this.slugs.form} submit`, + {event, fde}, + ) + }, + ...config.events, + } + + const $d = document + + const form = $d.createElement('form') + + this.elements.form = form + + form.name = `${this.slugs.form}` + form.classList.add('flex', 'col', 'card') + form.innerHTML = this.markup.content() + + this.api.on((apiChange) => { + console.log('CN Card API Change', apiChange) + this.render?.({}) + }) + + /** + * Update the config of the CN Card + * @function + */ + this.updateConfig = (config = {}) => { + console.log('CN Card updateConfig TOP', config) + + for (let param in config) { + this[param] = { + ...this[param], + ...(config[param] || {}), + } + } + + console.log('CN Card updateConfig BOT', this) + } + + /** + * Trigger the rendering of the CN Card + * @function + */ + this.render = ({ + cfg = {}, + position = 'afterend', + el = this.appElement, + }) => { + console.log('crowdnode render', this) + + this.elements.form?.removeEventListener?.( + 'submit', + this.events.submit, + ) + + if (el !== this.appElement) { + this.appElement = el + } + + this.updateConfig(cfg) + + this.elements.form.name = this.slugs.form + this.elements.form.innerHTML = this.markup.content() + + this.elements.form.addEventListener( + 'submit', + this.events.submit, + ) + + console.log('CARD RENDER', this, cfg) + + if (!this.state.rendered) { + el.insertAdjacentElement(position, this.elements.form) + this.state.rendered = this.elements.form + } + + this.events?.render?.(this) + } + + return this + } + + return cn +})(); + +export default CrowdNodeCard diff --git a/src/components/dialog.js b/src/components/dialog.js index b133a09..4551c39 100644 --- a/src/components/dialog.js +++ b/src/components/dialog.js @@ -4,6 +4,10 @@ import { envoy, } from '../helpers/utils.js' +import { + DIALOG_STATUS, +} from '../helpers/constants.js' + let modal = envoy( { rendered: {}, @@ -26,7 +30,8 @@ const initialState = { rendered: null, responsive: true, delay: 500, - async render () {}, + status: DIALOG_STATUS.NOT_LOADING, + async render () { return this }, addListener () {}, addListeners () {}, removeAllListeners (targets) {}, @@ -210,6 +215,9 @@ const initialState = { // event.target === state.elements.dialog, // state.elements.dialog.returnValue // ) + state.status = DIALOG_STATUS.NOT_LOADING + state.elements.progress?.remove() + state.elements.progress = null if (state.elements.dialog.returnValue !== 'cancel') { resolve(state.elements.dialog.returnValue) @@ -298,30 +306,30 @@ export async function setupDialog( .replaceAll(/[^a-zA-Z _]/g, '') .replaceAll(' ', '_') - const dialog = document.createElement('dialog') - const form = document.createElement('form') - const progress = document.createElement('progress') + const dialogElement = document.createElement('dialog') + const formElement = document.createElement('form') + const progressElement = document.createElement('progress') - state.elements.dialog = dialog - state.elements.form = form - state.elements.progress = progress + state.elements.dialog = dialogElement + state.elements.form = formElement + state.elements.progress = progressElement - progress.classList.add('pending') + progressElement.classList.add('pending') - dialog.innerHTML = `` - dialog.id = state.slugs.dialog + dialogElement.innerHTML = `` + dialogElement.id = state.slugs.dialog if (state.responsive) { - dialog.classList.add('responsive') + dialogElement.classList.add('responsive') } - dialog.classList.add(state.placement) + dialogElement.classList.add(...(state.placement.split(' '))) - form.name = `${state.slugs.form}` - form.method = 'dialog' - form.innerHTML = await state.content(state) + formElement.name = `${state.slugs.form}` + formElement.method = 'dialog' + formElement.innerHTML = await state.content(state) - dialog.insertAdjacentElement( + dialogElement.insertAdjacentElement( 'afterbegin', - form + formElement ) function addListener( @@ -340,74 +348,74 @@ export async function setupDialog( ) { if (resolve && reject) { addListener( - dialog, + dialogElement, 'close', state.events.handleClose(state, resolve, reject), ) addListener( - dialog, + dialogElement, 'click', state.events.handleClick(state), ) } addListener( - form, + formElement, 'blur', state.events.handleBlur(state), ) addListener( - form, + formElement, 'focusout', state.events.handleFocusOut(state), ) addListener( - form, + formElement, 'focusin', state.events.handleFocusIn(state), ) addListener( - form, + formElement, 'change', state.events.handleChange(state), ) - // let updrop = form.querySelector('.updrop') + // let updrop = formElement.querySelector('.updrop') // state.elements.updrop = updrop // if (updrop) { addListener( - form, + formElement, 'drop', state.events.handleDrop(state), ) addListener( - form, + formElement, 'dragover', state.events.handleDragOver(state), ) addListener( - form, + formElement, 'dragend', state.events.handleDragEnd(state), ) addListener( - form, + formElement, 'dragleave', state.events.handleDragLeave(state), ) // } addListener( - form, + formElement, 'input', state.events.handleInput(state), ) addListener( - form, + formElement, 'reset', state.events.handleReset(state), ) addListener( - form, + formElement, 'submit', state.events.handleSubmit(state), ) @@ -416,7 +424,7 @@ export async function setupDialog( state.addListeners = addListeners function removeAllListeners( - targets = [dialog,form], + targets = [dialogElement,formElement], ) { if (state.elements.updrop) { targets.push(state.elements.updrop) @@ -456,29 +464,56 @@ export async function setupDialog( } } - dialog.id = state.slugs.dialog - form.name = `${state.slugs.form}` - form.innerHTML = await state.content(state) + dialogElement.id = state.slugs.dialog + formElement.name = `${state.slugs.form}` + formElement.innerHTML = await state.content(state) // console.log('DIALOG RENDER', state, position, state.slugs.dialog, modal.rendered) + if ( + state.status === DIALOG_STATUS.LOADING && + state.elements.progress + ) { + state.elements.form.insertAdjacentElement( + 'beforebegin', + state.elements.progress, + ) + + // document.body.insertAdjacentHTML( + // 'afterbegin', + // ``, + // ) + } + + if ( + state.status === DIALOG_STATUS.SUCCESS || + state.status === DIALOG_STATUS.ERROR + ) { + // document.getElementById('pageLoader')?.remove() + state.elements.progress?.remove() + } + if (!modal.rendered[state.slugs.dialog]) { - el.insertAdjacentElement(position, dialog) - modal.rendered[state.slugs.dialog] = dialog + el.insertAdjacentElement(position, dialogElement) + modal.rendered[state.slugs.dialog] = dialogElement } state.events.handleRender(state) + + return state } state.render = render return { - element: dialog, + element: dialogElement, + state, + elements: state.elements, show: (callback) => new Promise((resolve, reject) => { removeAllListeners() addListeners(resolve, reject) - // console.log('dialog show', dialog) - dialog.show() + // console.log('dialog show', dialogElement) + dialogElement.show() state.events.handleShow?.(state) callback?.() }), @@ -486,11 +521,11 @@ export async function setupDialog( removeAllListeners() addListeners(resolve, reject) // console.log('dialog showModal', dialog) - dialog.showModal() + dialogElement.showModal() state.events.handleShow?.(state) callback?.() }), - close: returnVal => dialog.close(returnVal), + close: returnVal => dialogElement.close(returnVal), render, } } diff --git a/src/components/modal.js b/src/components/modal.js new file mode 100644 index 0000000..781e7a5 --- /dev/null +++ b/src/components/modal.js @@ -0,0 +1,543 @@ +import { lit as html } from '../helpers/lit.js' +import { + formDataEntries, + createSignal, + effect, + toSlug, + addListener, + addListeners, + removeAllListeners, +} from '../helpers/utils.js' + +import { + DIALOG_STATUS, +} from '../helpers/constants.js' + +/** + * Create a new HTML Dialog + */ +export const DialogContructor = (() => { + const initCfg = { + state: {}, + slugs: {}, + events: {}, + elements: {}, + templates: {}, + } + const initialState = { + id: 'Dialog', + name: 'Dialog', + submitTxt: 'Submit', + submitAlt: 'Submit Form', + cancelTxt: 'Cancel', + cancelAlt: `Cancel Form`, + closeTxt: 'X', + closeAlt: `Close`, + placement: 'center', + rendered: null, + responsive: true, + delay: 500, + status: DIALOG_STATUS.NOT_LOADING, + } + + /** + * Create a new HTML Dialog + * + * @param {Object} config + */ + const dl = function DialogModal( + config = {} + ) { + config = { + ...initCfg, + ...config, + } + + this.eventHandlers = [] + + this.state = createSignal({ + ...initialState, + ...config.state, + }) + + this.slugs = { + dialog: toSlug(this.state.value.name, this.state.value.id), + form: toSlug(this.state.value.id, this.state.value.name), + ...config.slugs, + } + + this.appElement = document.body + + this.elements = { + ...config.elements, + } + + this.markup = {} + + effect(() => { + this.markup.header = html` +
+ ${this.state.value.name} + ${ + this.state.value.closeTxt && html`` + } +
+ ` + this.markup.footer = html` + + ` + }) + + this.markup.fields = html` + + + +

Some instructions

+ ` + + this.markup.content = () => html` + ${this.markup.header} + +
+ ${this.markup.fields} + +
+
+ + ${this.markup.footer} + ` + + this.markup = { + ...this.markup, + ...config.markup, + } + + this.events = { + input: event => { + // let { + // // @ts-ignore + // name: fieldName, form, + // } = event?.target + + // console.log('handle input', { + // fieldName, + // form, + // }) + + if ( + event?.target?.validity?.patternMismatch && + event?.target?.type !== 'checkbox' + ) { + event.preventDefault() + let label = event.target?.previousElementSibling?.textContent?.trim() + if (label) { + event.target.setCustomValidity(`Invalid ${label}`) + } + } else { + event.target.setCustomValidity('') + } + event.target.reportValidity() + }, + change: event => { + // let { + // // @ts-ignore + // name: fieldName, form, + // } = event?.target + + // console.log('handle change', { + // fieldName, + // form, + // }) + + if ( + event?.target?.validity?.patternMismatch && + event?.target?.type !== 'checkbox' + ) { + event.preventDefault() + let label = event.target?.previousElementSibling?.textContent?.trim() + if (label) { + event.target.setCustomValidity(`Invalid ${label}`) + } + } else { + event.target.setCustomValidity('') + } + event.target.reportValidity() + }, + blur: event => { + // event.preventDefault() + // console.log( + // 'handle blur', + // event, + // ) + }, + focusout: event => { + // event.preventDefault() + // console.log( + // 'handle focus out', + // event, + // ) + }, + focusin: event => { + // event.preventDefault() + // console.log( + // 'handle focus in', + // event, + // ) + }, + drop: event => { + event.preventDefault() + }, + dragover: event => { + event.preventDefault() + }, + dragend: event => { + event.preventDefault() + }, + dragleave: event => { + event.preventDefault() + }, + render: ( + state, + ) => { + // console.log( + // 'handle dialog render', + // state, + // ) + }, + show: ( + state, + ) => { + // console.log( + // 'handle dialog show', + // state, + // ) + + // focus first input + this.elements.form.querySelector( + 'input' + )?.focus() + }, + close: ( + resolve = res=>{}, + reject = res=>{}, + ) => async event => { + event.preventDefault() + removeAllListeners.bind(this) + + // console.log( + // 'handle dialog close', + // event, + // event.target === this.elements.dialog, + // this.elements.dialog.returnValue + // ) + this.state.value.status = DIALOG_STATUS.NOT_LOADING + this.elements.dialog?.querySelector('progress')?.remove() + + if (this.elements.dialog.returnValue !== 'cancel') { + resolve(this.elements.dialog.returnValue) + } else { + resolve('cancel') + } + // console.log( + // 'DIALOG handleClose', + // modal.rendered[this.slugs.dialog], + // ) + + setTimeout(t => { + // modal.rendered[this.slugs.dialog] = null + this.state.value.rendered = null + event?.target?.remove() + // console.log( + // 'DIALOG handleClose setTimeout', + // this.state.value.delay, + // // modal.rendered[this.slugs.dialog], + // modal.rendered, + // ) + }, this.state.value.delay) + }, + submit: event => { + event.preventDefault() + + let fde = formDataEntries(event) + + this.elements.dialog.returnValue = String(fde.intent) + + // console.log( + // 'handleSubmit', + // [event], + // ) + + this.elements.dialog.close(String(fde.intent)) + }, + reset: event => { + event.preventDefault() + this.elements.form?.removeEventListener( + 'close', + this.events.reset + ) + // console.log( + // 'handleReset', + // [event.target], + // ) + this.elements.dialog.close('cancel') + }, + click: event => { + if (event.target === this.elements.dialog) { + // console.log( + // 'handle dialog backdrop click', + // event, + // event.target === this.elements.dialog + // ) + this.elements.dialog.close('cancel') + } + }, + } + + const dialogElement = document.createElement('dialog') + const formElement = document.createElement('form') + const progressElement = document.createElement('progress') + + this.elements.dialog = dialogElement + this.elements.form = formElement + this.elements.progress = progressElement + + + this.element = this.elements.dialog + + progressElement.classList.add('pending') + + dialogElement.innerHTML = `` + dialogElement.id = this.slugs.dialog + if (this.state.value.responsive) { + dialogElement.classList.add('responsive') + } + dialogElement.classList.add(...(this.state.value.placement.split(' '))) + + formElement.name = `${this.slugs.form}` + formElement.method = 'dialog' + // formElement.innerHTML = this.markup.content + formElement.innerHTML = this.markup.content() + + dialogElement.insertAdjacentElement( + 'afterbegin', + formElement + ) + + /** + * Show the dialog + * @function + */ + this.show = (callback, el = this.appElement) => new Promise((resolve, reject) => { + removeAllListeners.call(this) + addListeners.call(this, resolve, reject) + console.log('modal.js dialog show', this.elements.dialog) + // if ( + // !this.state.value.rendered && + // !el.contains(this.elements.dialog) + // ) { + this.render({ + el, + // cfg: config, + position: 'afterend' + }) + // } + this.elements.dialog.show() + this.events.show?.(this) + callback?.() + }) + + /** + * Show the Modal form of the dialog + * @function + */ + this.showModal = (callback, el = this.appElement) => new Promise((resolve, reject) => { + removeAllListeners.call(this) + addListeners.call(this, resolve, reject) + console.log('modal.js dialog showModal', this, this.elements.dialog) + // if ( + // !this.state.value.rendered && + // !el.contains(this.elements.dialog) + // ) { + this.render({ + el, + // cfg: config, + position: 'afterend' + }) + // } + this.elements.dialog.showModal() + this.events.show?.(this) + callback?.() + }) + + /** + * Close the dialog + * @function + */ + this.close = returnVal => this.elements.dialog.close(returnVal) + + /** + * Update the config of the dialog + * @function + */ + this.updateConfig = (config = {}) => { + console.log('Dialog updateConfig TOP', config) + + for (let param in config) { + // if (param === 'markup') { + // this[param].value = { + // ...this[param].value, + // ...(config[param] || {}), + // } + // } else { + if ('value' in this[param]) { + this[param].value = { + ...this[param].value, + ...(config[param] || {}), + } + } else { + this[param] = { + ...this[param], + ...(config[param] || {}), + } + } + // } + } + + console.log('Dialog updateConfig BOT', this) + } + + /** + * Trigger the rendering of the dialog + * @function + */ + this.render = ({ + cfg = {}, + position = 'afterend', + el = this.appElement, + }) => { + console.log('dialog render', this, dl) + + // this.elements.form?.removeEventListener?.( + // 'submit', + // this.events.submit, + // ) + + if (el !== this.appElement) { + this.appElement = el + } + + this.updateConfig(cfg) + + console.log('DIALOG elements', this, this.elements) + + this.elements.dialog.id = this.slugs.dialog + this.elements.form.name = this.slugs.form + // this.elements.form.innerHTML = this.markup.content + this.elements.form.innerHTML = this.markup.content() + + // this.elements.form.addEventListener( + // 'submit', + // this.events.submit, + // ) + + console.log('DIALOG RENDER STATE', this.state.value, cfg) + + console.log('DIALOG RENDER', position, this.slugs.dialog) + + if ( + this.state.value.status === DIALOG_STATUS.LOADING + ) { + this.elements.form.insertAdjacentElement( + 'beforebegin', + this.elements.progress, + ) + + // document.body.insertAdjacentHTML( + // 'afterbegin', + // ``, + // ) + } + + if ( + this.state.value.status === DIALOG_STATUS.SUCCESS || + this.state.value.status === DIALOG_STATUS.ERROR + ) { + // document.getElementById('pageLoader')?.remove() + this.elements.dialog.querySelector('progress')?.remove() + } + + if (!this.state.value.rendered) { + console.log('!this.state.rendered el', el, this.appElement, this.elements.dialog) + // @ts-ignore + el?.insertAdjacentElement(position, this.elements.dialog) + this.state.value.rendered = this.elements.dialog + + this.elements.dialog.addEventListener( + 'close', + this.events.close + ) + } + + // console.log('DIALOG RENDER', state, position, state.slugs.dialog, modal.rendered) + + this.events.render(this.state) + + return this + } + + // const updateSelf = (cfg, methods = []) => { + // for (let mthd of methods) { + // this[mthd] = { + // ...this[mthd], + // ...(cfg[mthd] || {}), + // } + // } + // } + // updateSelf(cfg, [ + // 'state', 'slugs', 'events', 'elements', 'templates' + // ]) + + // state.render = render + } + + return dl +})(); + +// export function Modal(config) { +// let dialog = new DialogContructor(config) + +// console.log('Modal.js Dialog', dialog) + +// return dialog +// } + +export default DialogContructor diff --git a/src/components/svg-sprite.js b/src/components/svg-sprite.js index 3fa2055..0c7184a 100644 --- a/src/components/svg-sprite.js +++ b/src/components/svg-sprite.js @@ -7,6 +7,10 @@ const initialState = { + diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 629a956..e85f271 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -142,3 +142,15 @@ export const USAGE = { RECEIVE, CHANGE, } + +const NOT_LOADING = 0 +const LOADING = 1 +const SUCCESS = 2 +const ERROR = 3 + +export const DIALOG_STATUS = { + NOT_LOADING, + LOADING, + SUCCESS, + ERROR +} \ No newline at end of file diff --git a/src/helpers/lit.js b/src/helpers/lit.js index f976e5a..fe7792f 100644 --- a/src/helpers/lit.js +++ b/src/helpers/lit.js @@ -1,8 +1,24 @@ /** + * Code Highlighting for String Literals. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#raw_strings MDN Reference} + * + * @example + * import { lit as html, lit as css } from './utils.js' + * let h = html`
${example}
` + * let c = css`div > span { color: #bad; }` + * + * // falsey values now default to empty string + * let falsey = html`
${doesNotExist && html``}
` + + * // falsey === '
' + * // instead of + * // falsey === '
undefined
' * * @param {TemplateStringsArray} s * @param {...any} v * * @returns {string} */ -export const lit = (s, ...v) => String.raw({ raw: s }, ...v) +export const lit = (s, ...v) => String.raw({ raw: s }, ...(v.map(x => x || ''))) +// export const lit = (s, ...v) => String.raw({ raw: s }, ...v) diff --git a/src/helpers/utils.js b/src/helpers/utils.js index 493808f..f613de1 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -13,6 +13,10 @@ import { SECONDS, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, } from './constants.js' +let currentSignal; +let globalDerivedValue; +let eventHandlers = [] + /** * * @param {String} [phraseOrXkey] @@ -446,14 +450,14 @@ export function envoy(obj, ...initListeners) { * document.querySelector("body").innerHTML = value; * }); * - * off(); // unsubscribe + * off(); * * @param {Object} initialValue inital value */ export function createSignal(initialValue) { let _value = initialValue; let _last = _value; - const subs = []; + let subs = []; function pub() { for (let s of subs) { @@ -461,18 +465,186 @@ export function createSignal(initialValue) { } } + function unsub(fn) { + for (let i in subs) { + if (subs[i] === fn) { + subs[i] = 0; + // break; + } + } + } + + function on(s) { + const i = subs.push(s)-1; + return () => { subs[i] = 0; }; + } + + function once(s) { + const i = subs.length + + subs.push((_value, _last) => { + s && s(_value, _last); + subs[i] = 0; + }); + } + return { - get value() { return _value; }, + get value() { + if ( + currentSignal //&& + // currentSignal !== globalDerivedValue + ) { + console.log( + 'currentSignal === derived', + {currentSignal, derived, globalDerivedValue}, + currentSignal === globalDerivedValue, + ) + on(currentSignal) + } + return _value; + }, set value(v) { _last = _value _value = v; pub(); }, - on: s => { - const i = subs.push(s)-1; - return () => { subs[i] = 0; }; + on, + once, + unsub, + } +} + +/** + * Use a reactive signal in hook fashion + * + * @example + * let [count, setCount, on] = useSignal(0) + * console.log(count()) // 0 + * setCount(2) + * console.log(count()) // 2 + * + * let off = on(value => { + * document.querySelector("body").innerHTML = value; + * }); + * + * off() + * + * @param {Object} initialValue inital value +*/ +export function useSignal(initialValue) { + let _value = initialValue; + let _last = _value; + let subs = []; + + function pub() { + for (let s of subs) { + s && s(_value, _last); + } + } + + function unsub(fn) { + for (let i in subs) { + if (subs[i] === fn) { + subs[i] = 0; + // break; + } } } + + function getValue(v) { + if ( + currentSignal //&& + // currentSignal !== globalDerivedValue + ) { + on(currentSignal) + } + return _value; + } + + function setValue(v) { + _last = _value + _value = v; + pub(); + } + + function on(s) { + const i = subs.push(s)-1; + return () => { subs[i] = 0; }; + } + + function once(s) { + const i = subs.length + + subs.push((_value, _last) => { + s && s(_value, _last); + subs[i] = 0; + }); + } + + return [ + // _value, + getValue, + setValue, + on, + once, + unsub, + ] +} + +/** + * {@link https://youtu.be/t18Kzj9S8-M?t=351 Understanding Signals} + * + * {@link https://youtu.be/1TSLEzNzGQM Learn Why JavaScript Frameworks Love Signals By Implementing Them} + * + * @example + * const [count, setCount] = useSignal(10) + * effect(() => console.log(count())) + * setCount(25) + * + * let letter = createSignal('a') + * effect(() => console.log(letter.value)) + * letter.value = 'b' + * + * @param {Function} fn + */ +export function effect(fn) { + currentSignal = fn; + + fn(); + + currentSignal = null; + + return fn +} + +/** + * {@link https://youtu.be/1TSLEzNzGQM Learn Why JavaScript Frameworks Love Signals By Implementing Them} + * + * @example + * let count = createSignal(10) + * let double = derived(() => count.value * 2) + * + * effect( + * () => console.log( + * count.value, + * double.value, + * ) + * ) + * + * count.value = 25 + * + * @param {Function} fn + */ +export function derived(fn) { + const derived = createSignal() + + globalDerivedValue = function derivedValue() { + derived.value = fn() + } + + effect(globalDerivedValue) + + return derived } export async function restate( @@ -934,6 +1106,20 @@ export function nobounce(callback, delay = 300) { } } + +/** + * @example + * await forIt(500); + * nowDoThis() + * + * @param {number} [delay] +* +* @returns {Promise} +*/ +export function forIt(delay) { + return new Promise(resolve => setTimeout(resolve, delay)); +} + export function timeago(ms, locale = TIMEAGO_LOCALE_EN) { var ago = Math.floor(ms / 1000); var part = 0; @@ -1213,3 +1399,118 @@ export function getAddressIndexFromUsage(wallet, account, usageIdx) { addressIndex, } } + +export function toSlug(...slugs) { + return slugs.join('_').toLowerCase() + .replaceAll(/[^a-zA-Z _]/g, '') + .replaceAll(' ', '_') +} + +export function addListener( + node, + event, + handler, + capture = false, + handlers = this?.eventHandlers || eventHandlers, +) { + console.log('addListener', this, { node, event, handler, capture }) + handlers.push({ node, event, handler, capture }) + node.addEventListener(event, handler, capture) +} + +export function addListeners( + resolve, + reject, +) { + if (resolve && reject) { + addListener( + this.elements.dialog, + 'close', + this.events.close(resolve, reject), + ) + + addListener( + this.elements.dialog, + 'click', + this.events.click, + ) + } + + addListener( + this.elements.form, + 'blur', + this.events.blur, + ) + addListener( + this.elements.form, + 'focusout', + this.events.focusout, + ) + addListener( + this.elements.form, + 'focusin', + this.events.focusin, + ) + addListener( + this.elements.form, + 'change', + this.events.change, + ) + // if (updrop) { + addListener( + this.elements.form, + 'drop', + this.events.drop, + ) + addListener( + this.elements.form, + 'dragover', + this.events.dragover, + ) + addListener( + this.elements.form, + 'dragend', + this.events.dragend, + ) + addListener( + this.elements.form, + 'dragleave', + this.events.dragleave, + ) + // } + addListener( + this.elements.form, + 'input', + this.events.input, + ) + addListener( + this.elements.form, + 'reset', + this.events.reset, + ) + addListener( + this.elements.form, + 'submit', + this.events.submit, + ) +} + +export function removeAllListeners( + targets = [ + this?.elements.dialog, + this?.elements.form, + ], + handlers = this?.eventHandlers || eventHandlers, +) { + if (this.elements.updrop) { + targets.push(this.elements.updrop) + } + handlers = handlers + .filter(({ node, event, handler, capture }) => { + if (targets.includes(node)) { + node.removeEventListener(event, handler, capture) + return false + } + return true + }) +} diff --git a/src/imports.js b/src/imports.js index 9057285..81fb104 100644 --- a/src/imports.js +++ b/src/imports.js @@ -11,6 +11,7 @@ * * See https://github.com/jojobyte/browser-import-rabbit-hole */ +import './secp.js'; import '../node_modules/dashtx/dashtx.js'; import '../node_modules/dashkeys/dashkeys.js'; @@ -20,7 +21,7 @@ import '../node_modules/dashsight/dashsight.js'; import '../node_modules/dashsight/dashsocket.js'; import '../node_modules/@dashincubator/base58check/base58check.js'; import '../node_modules/@dashincubator/ripemd160/ripemd160.js'; -import '../node_modules/@dashincubator/secp256k1/secp256k1.js'; +// import '../node_modules/@dashincubator/secp256k1/secp256k1.js'; import '../node_modules/crypticstorage/cryptic.js'; import '../node_modules/dashwallet/dashwallet.js'; import '../node_modules/localforage/dist/localforage.js'; @@ -56,7 +57,10 @@ export let Base58Check = window?.Base58Check || globalThis?.Base58Check; /** @type {RIPEMD160Types} */ export let RIPEMD160 = window?.RIPEMD160 || globalThis?.RIPEMD160; /** @type {Secp256k1Types} */ -export let Secp256k1 = window?.nobleSecp256k1 || globalThis?.nobleSecp256k1; +export let Secp256k1 = ( + window?.nobleSecp256k1 || globalThis?.nobleSecp256k1 || + window?.Secp256k1 || globalThis?.Secp256k1 +); /** @type {CrypticTypes} */ export let Cryptic = window?.Cryptic || globalThis?.Cryptic; diff --git a/src/index.css b/src/index.css index afa2fa0..11382a1 100644 --- a/src/index.css +++ b/src/index.css @@ -96,16 +96,35 @@ figure h4 { font-size: 2rem; } -.mh-25 { +.min-h-auto { + min-height: auto; +} +.min-h-0 { + min-height: 0; +} +.min-h-25 { + min-height: 25%; +} +.min-h-50 { + min-height: 50%; +} +.min-h-75 { + min-height: 75%; +} +.min-h-100 { + min-height: 100%; +} + +.max-h-25 { max-height: 25%; } -.mh-50 { +.max-h-50 { max-height: 50%; } -.mh-75 { +.max-h-75 { max-height: 75%; } -.mh-100 { +.max-h-100 { max-height: 100%; } @@ -167,9 +186,24 @@ figure h4 { .jc-start { justify-content: start; } +.jc-center { + justify-content: center; +} +.jc-right { + justify-content: right; +} .jc-end { justify-content: end; } +.jc-around { + justify-content: space-around; +} +.jc-between { + justify-content: space-between; +} +.jc-evenly { + justify-content: space-evenly; +} .as-start { align-self: start; } @@ -320,6 +354,28 @@ figure h4 { font-size: 1.5rem; } +.p-0 { + padding: 0; +} +.p-1 { + padding: .25rem; +} +.p-2 { + padding: .75rem; +} +.p-3 { + padding: 1rem; +} +.p-4 { + padding: 1.5rem; +} +.p-5 { + padding: 2rem; +} +.p-6 { + padding: 2.5rem; +} + .px-0 { padding-left: 0; padding-right: 0; diff --git a/src/main.js b/src/main.js index 3f06a08..f23ab8e 100644 --- a/src/main.js +++ b/src/main.js @@ -6,10 +6,16 @@ import { getStoreData, loadStoreObject, formDataEntries, + // forIt, + // useSignal, + // createSignal, + // effect, + // derived, } from './helpers/utils.js' import { DUFFS, + DIALOG_STATUS, } from './helpers/constants.js' import { @@ -79,6 +85,8 @@ import pairQrRig from './rigs/pair-qr.js' import txInfoRig from './rigs/tx-info.js' import showErrorDialog from './rigs/show-error.js' +import crowdnodeTransactionRig from './rigs/crowdnode-tx.js' + // app/data state let accounts let wallets @@ -431,6 +439,40 @@ async function showNotification({ console.log('notification', {type, title, msg, sticky}) } +async function showQrCode(state = {}) { + let initState = { + name: 'Share to receive funds', + submitTxt: `Edit Amount or Contact`, + submitAlt: `Change the currently selected contact`, + footer: state => html` + + `, + amount: 0, + wallet, + contacts: appState.contacts, + ...state, + } + + let showRequestQRRender = await appDialogs.requestQr.render( + initState, + 'afterend', + ) + + let showRequestQR = await appDialogs.requestQr.showModal() + + return showRequestQRRender +} + async function main() { appState.encryptionPassword = window.atob( sessionStorage.encryptionPassword || '' @@ -673,32 +715,9 @@ async function main() { // } // ) - await appDialogs.requestQr.render( - { - name: 'Share to receive funds', - submitTxt: `Edit Amount or Contact`, - submitAlt: `Change the currently selected contact`, - footer: state => html` - - `, - amount: 0, - wallet, - contacts: appState.contacts, - }, - 'afterend', - ) + showQrCode() - let showRequestQR = await appDialogs.requestQr.showModal() + // let showRequestQR = await appDialogs.requestQr.showModal() } } else { await appDialogs.sendOrReceive.render({ @@ -766,6 +785,7 @@ async function main() { res.push(await appTools.storedData?.decryptData?.(v) || v) }, ) + console.log('appState.contacts', appState.contacts) await contactsList.render({ userInfo, @@ -790,23 +810,301 @@ async function main() { walletFunds, }) }) + import('./components/crowdnode-card.js') + .then(async ({ CrowdNodeCard }) => { + // let crowdnodeTransaction = crowdnodeTransactionRig + console.log( + 'crowdnodeTransactionRig', + crowdnodeTransactionRig, + crowdnodeTransactionRig.markup.fields, + ) + // crowdnodeTransactionRig.markup.fields + let cfg = { + state: { + // name: '', + }, + events: { + submit: async event => { + event.preventDefault() + event.stopPropagation() - integrationsSection.insertAdjacentHTML('beforeend', html` -
-
-
Coming soon
-

Earn interest with

-
-
- - - - - - -
-
- `) + // this.elements.form?.removeEventListener('submit', this.events.submit) + + let fde = formDataEntries(event) + + console.log( + `Crowdnode Card submit`, + {event, fde}, + ) + + if (fde.intent === 'signup') { + let confAct = await appDialogs.confirmAction.render({ + name: 'Signup for Crowdnode', + actionTxt: 'Signup', + actionAlt: 'Signup for Crowdnode', + action: '', + actionType: 'infoo', + placement: 'center auto-height', + // status: DIALOG_STATUS.LOADING, + acceptedToS: false, + submitIcon: () => ``, + alert: state => html` +
+ +

This process may take a while, please be patient.

+
+ `, + fields: () => html` +
+
+ To stake your Dash and begin earning interest, read and accept the CrowdNode Terms and Conditions. + Funds are required to complete the signup process. +
+
+ `, + callback: async (state, fde) => { + state.status = DIALOG_STATUS.LOADING + + if (fde?.acceptToS === 'on') { + state.acceptedToS = true + } + + let cbConfAct = await appDialogs.confirmAction.render(state) + + console.log( + `confirm action`, + {state, fde, cbConfAct}, + ) + + let cnFunding = await showQrCode({ + name: 'CrowdNode Funding', + amount: 1.1, + status: DIALOG_STATUS.LOADING, + // wallet, + // contacts: appState.contacts, + // submitTxt: `Fund your wallet`, + // submitAlt: `Change the currently selected contact`, + generateNextAddress: state => { + // return html`` + return html` + Send 1.1 Dash or more
+ to signup & fund your CrowdNode account. + ` + }, + footer: state => html` + + `, + }) + + // await forIt(5000) + + state.status = DIALOG_STATUS.SUCCESS + + cbConfAct = await appDialogs.confirmAction.render(state) + + console.log('CN Card Funding Callback', cnCard) + + cnCard.api.value = { + acceptedToS: true, + balance: 1.234, + earned: 0.987, + } + + cnCard.render({ + cfg, + el: integrationsSection, + position: 'beforeend' + }) + + console.log( + `confirm action SUCCESS`, + {state, fde, cnFunding}, + ) + + return { state, fde } + }, + }) + + // let confAct = appDialogs.confirmAction.render({ + // el: mainApp, + // cfg: { + // appElement: mainApp, + // state: { + // name: 'Signup for Crowdnode', + // actionTxt: 'Signup', + // actionAlt: 'Signup for Crowdnode', + // action: '', + // actionType: 'infoo', + // placement: 'center auto-height', + // // status: DIALOG_STATUS.LOADING, + // }, + // markup: { + // submitIcon: ``, + // alert: html` + //
+ // + //

This process may take a while, please be patient.

+ //
+ // `, + // fields: html` + //
+ //
+ // To stake your Dash and begin earning interest, read and accept the CrowdNode Terms and Conditions. + // Funds are required to complete the signup process. + //
+ //
+ // `, + // }, + // callback: async (state, fde) => { + // state.status = DIALOG_STATUS.LOADING + + // let cbConfAct = await appDialogs.confirmAction.render(state) + + // console.log( + // `confirm action`, + // {state, fde, cbConfAct}, + // ) + + // await forIt(5000) + + // state.status = DIALOG_STATUS.SUCCESS + + // console.log( + // `confirm action SUCCESS`, + // {state, fde}, + // ) + + // return { state, fde } + // }, + // }, + // }) + + console.log('CN Card confAct Submit Event', cnCard) + console.log('confAct', confAct, appDialogs.confirmAction) + + confAct?.elements?.form?.classList.add?.('min-h-auto') + + appDialogs.confirmAction.showModal() + } + + console.log( + `Crowdnode Card submit TX`, + fde.intent, + {event, fde}, + ) + + if (fde.intent === 'deposit') { + appDialogs.sendOrReceive?.elements?.form?.classList.add?.('min-h-auto') + await appDialogs.sendOrReceive.render({ + name: 'Deposit to CrowdNode', + cashSend: () => html``, + hideAddressee: true, + action: fde.intent, + wallet, + account: appState.account, + userInfo, + contacts: appState.contacts, + to: '@crowdnode', + }) + appDialogs.sendOrReceive.showModal() + } + if (fde.intent === 'withdraw') { + crowdnodeTransactionRig.markup.fields = html` +
+ + +
+ Enter the percentage you wish to unstake. + ` + + let cnWithdraw = crowdnodeTransactionRig.render({ + el: mainApp, + cfg: { + state: { + name: 'Withdraw from CrowdNode', + submitTxt: 'Withdraw', + submitAlt: 'Withdraw funds from CrowdNode', + cancelTxt: 'Cancel', + cancelAlt: `Cancel Withdraw`, + }, + }, + }) + cnWithdraw?.elements?.form?.classList.add?.('min-h-auto') + + console.log( + `Crowdnode Card TX`, + fde.intent, + {cnWithdraw}, + ) + crowdnodeTransactionRig.showModal() + // cnWithdraw.showModal() + } + } + }, + } + + let cnCard = new CrowdNodeCard(cfg) + console.log('CN Card Outer', cnCard) + + // cfg.events.submit = + + // cnCard.updateConfig(cfg) + + cnCard.render({ + cfg, + el: integrationsSection, + position: 'beforeend' + }) + }) + + + // integrationsSection.insertAdjacentHTML('beforeend', html` + //
+ //
+ //
Coming soon
+ //

Earn interest with

+ //
+ //
+ // + // + // + // + // + // + //
+ //
+ // `) let txs = await getTxs( appState, @@ -895,7 +1193,7 @@ async function main() { action: 'lock', actionType: 'warn', alert: state => html``, - callback: () => { + callback: async () => { sessionStorage.clear() window.location.reload() }, @@ -925,7 +1223,7 @@ async function main() { This is an irreversable action which removes all wallet data from your browser, make sure to backup your data first.

WE RETAIN NO BACKUPS OF YOUR WALLET DATA.

`, - callback: () => { + callback: async () => { localStorage.clear() sessionStorage.clear() // @ts-ignore diff --git a/src/rigs/confirm-action.js b/src/rigs/confirm-action.js index 1e43ca1..4d220a1 100644 --- a/src/rigs/confirm-action.js +++ b/src/rigs/confirm-action.js @@ -3,6 +3,10 @@ import { formDataEntries, } from '../helpers/utils.js' +import { + DIALOG_STATUS, +} from '../helpers/constants.js' + export let confirmActionRig = (async function (globals) { 'use strict'; @@ -26,6 +30,7 @@ export let confirmActionRig = (async function (globals) { actionType: 'warn', actionClasses: { info: 'bg-info dark bg-info-hover', + infoo: 'outline brd-info info dark light-hover bg-info-hover', warn: 'outline brd-warn warn dark-hover bg-warn-hover', dang: 'outline brd-dang dang light-hover bg-dang-hover', }, @@ -79,16 +84,7 @@ export let confirmActionRig = (async function (globals) { `, alert: state => html``, - // alert: state => html` - //
- // - // This is an irreversable action, make sure to backup first. - // - //
- // `, - content: state => html` - ${state.header(state)} - + fields: state => html`
Are you sure you want to ${state.action} ${ @@ -96,6 +92,11 @@ export let confirmActionRig = (async function (globals) { }?
+ `, + content: state => html` + ${state.header(state)} + + ${state.fields(state)} ${state.footer(state)} `, @@ -108,8 +109,17 @@ export let confirmActionRig = (async function (globals) { if (fde?.intent === 'act') { // state.elements.dialog.returnValue = String(fde.intent) - state.callback?.(state, fde) - confirmAction.close(fde.intent) + let res = await state.callback?.(state, fde) + + if ( + res.state.status === DIALOG_STATUS.SUCCESS || + res.state.status === DIALOG_STATUS.ERROR + ) { + // state.elements.dialog?.querySelector('progress')?.remove() + state.elements.progress?.remove?.() + + confirmAction.close(fde.intent) + } } }, }, diff --git a/src/rigs/crowdnode-tx.js b/src/rigs/crowdnode-tx.js new file mode 100644 index 0000000..66b86c4 --- /dev/null +++ b/src/rigs/crowdnode-tx.js @@ -0,0 +1,135 @@ +import { lit as html } from '../helpers/lit.js' +import { + formDataEntries, +} from '../helpers/utils.js' + +import { + DIALOG_STATUS, +} from '../helpers/constants.js' + +import { DialogContructor } from '../components/modal.js' + +export const crowdnodeTransactionRig = (() => { + 'use strict'; + + // let { + // mainApp, setupDialog, + // } = globals + + let dialogConfig = { + state: { + name: 'CrowdNode Deposit / Withdraw', + actionTxt: 'Do It!', + actionAlt: 'Yeah, really do this!', + cancelTxt: 'Cancel', + cancelAlt: `Cancel`, + closeTxt: html``, + closeAlt: `Cancel & Close`, + action: '', + target: '', + targetFallback: 'this wallet', + actionType: 'warn', + actionClasses: { + info: 'bg-info dark bg-info-hover', + infoo: 'outline brd-info info dark light-hover bg-info-hover', + warn: 'outline brd-warn warn dark-hover bg-warn-hover', + dang: 'outline brd-dang dang light-hover bg-dang-hover', + }, + showCancelBtn: true, + showActBtn: true, + }, + markup: {}, + events: {}, + // appElement: mainApp, + appElement: document.body, + } + + let crowdnodeTransaction = new DialogContructor(dialogConfig) + + console.log('Modal.js Dialog', crowdnodeTransaction) + + dialogConfig.events.submit = async function (event) { + event.preventDefault() + event.stopPropagation() + + let fde = formDataEntries(event) + + if (fde?.intent === 'act') { + // state.elements.dialog.returnValue = String(fde.intent) + let res = await this.state.value.callback?.(this.state.value, fde) + + if ( + res.state.value.status === DIALOG_STATUS.SUCCESS || + res.state.value.status === DIALOG_STATUS.ERROR + ) { + // this.elements.dialog?.querySelector('progress')?.remove() + this.elements.progress?.remove?.() + + this.elements.dialog?.close(fde.intent) + } + } + } + + dialogConfig.markup.alert = html`` + dialogConfig.markup.submitIcon = `` + // dialogConfig.markup.submitIcon = html` + // + // + // + // ` + // dialogConfig.markup.cancelBtn = html` + // + // ` + // dialogConfig.markup.actionBtn = html` + // + // ` + // dialogConfig.markup.footer = html` + // + // ` + // dialogConfig.markup.fields = html` + //
+ // + // Are you sure you want to ${crowdnodeTransaction.state.value.action} ${ + // crowdnodeTransaction.state.value.target || crowdnodeTransaction.state.value.targetFallback + // }? + // + //
+ // ` + // dialogConfig.markup.content = () => html` + // ${crowdnodeTransaction.markup.header} + + // ${crowdnodeTransaction.markup.fields} + + // ${crowdnodeTransaction.markup.footer} + // ` + + crowdnodeTransaction.updateConfig(dialogConfig) + + return crowdnodeTransaction +})(); + +export default crowdnodeTransactionRig \ No newline at end of file diff --git a/src/rigs/send-or-request.js b/src/rigs/send-or-request.js index a2d94ac..ac290f2 100644 --- a/src/rigs/send-or-request.js +++ b/src/rigs/send-or-request.js @@ -38,6 +38,7 @@ export let sendOrReceiveRig = (async function (globals) { closeTxt: html``, closeAlt: `Close`, action: 'send', + hideAddressee: false, submitIcon: state => { const icon = { send: html` @@ -180,26 +181,56 @@ export let sendOrReceiveRig = (async function (globals) { ` }, + cashSend: state => html` +
+ + + +
+ `, + addressee: state => { + if (state.hideAddressee) { + return html` + + ` + } + + return html` +
+ + + ${state.qrScanBtn(state)} +
+ ` + }, content: state => html` ${state.header(state)}
-
- - - ${state.qrScanBtn(state)} -
+ ${state.addressee(state)}
-
- - - -
+ ${state.cashSend(state)}
@@ -444,6 +464,10 @@ export let sendOrReceiveRig = (async function (globals) { inWallet = Object.values(contact?.incoming)?.[0] } + console.log('send or request', { + to, contact, outWallet, inWallet + }) + if (!inWallet) { // state.wallet.addressIndex = ( // state.wallet?.addressIndex ?? -1 @@ -518,7 +542,7 @@ export let sendOrReceiveRig = (async function (globals) { } let leftoverBalance = walletFunds.balance - amount - let fullTransfer = leftoverBalance <= 0.0010_0200 + let fullTransfer = leftoverBalance > 0 && leftoverBalance <= 0.0010_0200 // let fullTransfer = leftoverBalance <= 0.0001_0200 if ( @@ -542,6 +566,7 @@ export let sendOrReceiveRig = (async function (globals) { state.wallet.accountIndex, state.wallet.addressIndex, ) + let amountNeeded = fixedDash(roundUsing(Math.floor, Math.abs( walletFunds.balance - Number(fde.amount) ))) diff --git a/src/rigs/show-error.js b/src/rigs/show-error.js index 0e590d5..b59d84f 100644 --- a/src/rigs/show-error.js +++ b/src/rigs/show-error.js @@ -60,7 +60,7 @@ export async function showErrorDialog(options) { content: state => html` ${state.header(state)} -
+
diff --git a/src/secp.js b/src/secp.js new file mode 100644 index 0000000..f49b0e8 --- /dev/null +++ b/src/secp.js @@ -0,0 +1,11 @@ +import '../node_modules/@dashincubator/secp256k1/secp256k1.js'; + +// @ts-ignore +const secp = window?.nobleSecp256k1 || globalThis?.nobleSecp256k1 || window?.Secp256k1 || globalThis?.Secp256k1 + +// @ts-ignore +window.Secp256k1 = secp + +export const Secp256k1 = secp + +export default secp diff --git a/src/styles/components.css b/src/styles/components.css index 43c7933..6751960 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -116,6 +116,9 @@ display: flex; } +.integration-sect { + padding: 0 1rem; +} .integration-sect > section > header { flex-direction: column; align-items: start; @@ -140,6 +143,34 @@ text-decoration: none; } +.card { + border-radius: 1rem; + overflow: auto; + flex: 1 1 auto; + max-width: 49%; + overflow: hidden; +} +.card header, +.card footer { + padding: 1rem 0; +} + +.card header a, +.card header a:active, +.card header a:focus, +.card header a:hover { + color: var(--fc); + text-decoration: none; +} +.card section { + padding: .25rem 0; +} +.card fieldset { + min-width: auto; +} +.card footer button { +} + @@ -150,6 +181,21 @@ .cols > section { padding: 0; } + + .card { + max-width: 32.5%; + padding: 0 1rem; + } + .card header, + .card footer { + padding: 1rem 0; + } + .card section { + padding: .25rem 0; + } + .integration-sect { + padding: 0; + } } @media (min-width: 980px) { .cols { diff --git a/src/styles/dialog.css b/src/styles/dialog.css index 75c3b1b..25ecf42 100644 --- a/src/styles/dialog.css +++ b/src/styles/dialog.css @@ -197,7 +197,7 @@ dialog > form > fieldset input + p { font-weight: 400; margin: .5rem 0; } -dialog > form > fieldset p { +dialog > form fieldset p { display: flex; padding: 0; margin: .8rem; @@ -217,7 +217,7 @@ dialog > form > fieldset div + p { margin: 0 0 0 1rem; text-align: left; } -dialog > form > fieldset p > span { +dialog > form fieldset p > span { padding: .25rem; border: 1px solid var(--dark-500); } diff --git a/src/styles/form.css b/src/styles/form.css index c1fc1b1..d1a4aca 100644 --- a/src/styles/form.css +++ b/src/styles/form.css @@ -494,7 +494,7 @@ label { } label > input[type=checkbox], label + input[type=checkbox] { - width: 2rem; + width: 1.25rem; } div.switch { @@ -739,6 +739,25 @@ form[name="network"] { cursor: default; } +main form header { + margin: 0 auto; + min-height: auto; + /* width: 100%; + display: flex; + justify-content: space-between; + align-items: flex-start; + flex-direction: column-reverse; */ +} + +.card footer { + gap: .25rem; +} + +.card footer button { + padding: 0.63rem .25rem; + flex: 1 1 50%; +} + @media (min-width: 650px) { main form { @@ -776,4 +795,8 @@ form[name="network"] { form > article > figure figcaption + div.big { font-size: 3.75rem; } */ + + .card footer button { + padding: 0.63rem 1rem; + } } \ No newline at end of file diff --git a/src/styles/motion.css b/src/styles/motion.css index 26aa428..85cff62 100644 --- a/src/styles/motion.css +++ b/src/styles/motion.css @@ -1,16 +1,16 @@ @-webkit-keyframes await-progress { - 0% { - background-position: -322px; + 0% { + background-position: -150%; } 100% { - background-position: 322px; + background-position: 200%; } } @keyframes await-progress { - 0% { - background-position: -322px; + 0% { + background-position: -150%; } 100% { - background-position: 322px; + background-position: 200%; } } diff --git a/src/styles/progress.css b/src/styles/progress.css index 1ad2c4f..6d30564 100644 --- a/src/styles/progress.css +++ b/src/styles/progress.css @@ -1,19 +1,3 @@ -progress:indeterminate { - background: linear-gradient( - 90deg, - #0000 0%, - #0000 50%, - var(--info) 100% - ); -} -progress.recording:indeterminate { - background: linear-gradient( - 90deg, - var(--livea) 0%, - var(--live) 50%, - var(--livea) 100% - ); -} progress.pending, progress.pending[role] { position: absolute; @@ -26,14 +10,15 @@ progress.pending[role] { padding: 0; border: none; background-color: transparent; - background-size: auto; + /* background-size: auto; */ + background-size: 50% 100%; background-repeat: no-repeat; background-position: 0 0; appearance: none; -moz-appearance: none; -webkit-appearance: none; border: 0 solid transparent; - visibility: hidden; + /* visibility: hidden; */ } progress.recording, progress.recording[role] { @@ -52,11 +37,3 @@ progress.pending:indeterminate { -webkit-animation: await-progress 2.5s ease-in-out infinite; animation: await-progress 2.5s ease-in-out infinite; } - - -/* form[name=toggle_relay]:has(input[type=checkbox]:checked) { - border-bottom: .25rem solid var(--dang); -} */ -form[name=toggle_relay]:has(input[type=checkbox]:checked) progress { - visibility: visible; -} diff --git a/src/styles/theme.css b/src/styles/theme.css index c8e5830..55d1c2e 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -19,6 +19,10 @@ --light-500: #004470; --light-600: #003e66; + --card-100: hsl(200deg 80% 60% / 15%); + --card-100: #00447066; + + --grayscale-gray-400: #9DA6A6; --c: var(--dark-900); @@ -140,11 +144,11 @@ a:hover { } svg { - fill: currentColor; vertical-align: bottom; } -svg path { +svg g:not(.lock), +svg path:not(.lock) { fill: currentColor; } @@ -316,6 +320,11 @@ th { border-radius: 6.25rem; } +.card { + background-color: var(--card-100); + /* background-color: var(--dark-600); */ +} + .cols > section > div { background-color: var(--nav-bg); } From 5906372a7994cad32c530aa3fb58c6bdbe5dac79 Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Fri, 20 Sep 2024 04:41:36 -0600 Subject: [PATCH 03/12] refactor: :recycle: add CrowdNode lib to imports & cleanup --- src/helpers/constants.js | 46 ++++++++++++++++++++++- src/imports.js | 17 +++++++-- src/index.html | 1 + src/rigs/crowdnode-tx.js | 80 ++++++++-------------------------------- src/styles/dialog.css | 3 ++ 5 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/helpers/constants.js b/src/helpers/constants.js index e85f271..d7a8d1f 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -153,4 +153,48 @@ export const DIALOG_STATUS = { LOADING, SUCCESS, ERROR -} \ No newline at end of file +} + +export const CROWDNODE = { + offset: 20000, + duffs: 100000000, + satoshis: 100000000, + depositMinimum: 100000, + stakeMinimum: 50000000, + + /** + * @type {Record} + */ + requests: { + acceptTerms: 65536, + offset: 20000, + signupForApi: 131072, + toggleInstantPayout: 4096, + withdrawMin: 1, + withdrawMax: 1000, + }, + + /** + * @type {Record} + */ + messages: { + PleaseAcceptTerms: 2, + WelcomeToCrowdNodeBlockChainAPI: 4, + DepositReceived: 8, + WithdrawalQueued: 16, + WithdrawalFailed: 32, + AutoWithdrawalEnabled: 64, + AutoWithdrawalDisabled: 128, + }, + + /** + * @type {Record} + */ + responses: {}, +} + +CROWDNODE.responses = Object.fromEntries( + Object.entries(CROWDNODE.messages).map( + ([k,v]) => [v,k] + ) +) diff --git a/src/imports.js b/src/imports.js index 81fb104..985bf1c 100644 --- a/src/imports.js +++ b/src/imports.js @@ -25,6 +25,9 @@ import '../node_modules/@dashincubator/ripemd160/ripemd160.js'; import '../node_modules/crypticstorage/cryptic.js'; import '../node_modules/dashwallet/dashwallet.js'; import '../node_modules/localforage/dist/localforage.js'; +import '../node_modules/crowdnode/dashcore-lit.js'; +import '../node_modules/crowdnode/dashapi.js'; +import '../node_modules/crowdnode/crowdnode.js'; import * as DashTxTypes from '../node_modules/dashtx/dashtx.js'; import * as DashKeysTypes from '../node_modules/dashkeys/dashkeys.js'; @@ -38,6 +41,9 @@ import * as Secp256k1Types from '../node_modules/@dashincubator/secp256k1/secp25 import * as CrypticTypes from '../node_modules/crypticstorage/cryptic.js'; import * as CrypticStorageTypes from '../node_modules/crypticstorage/storage.js'; import * as DashWalletTypes from '../node_modules/dashwallet/dashwallet.js'; +import * as DashCoreTypes from '../node_modules/crowdnode/dashcore-lit.js'; +import * as DashApiTypes from '../node_modules/crowdnode/dashapi.js'; +import * as CrowdNodeTypes from '../node_modules/crowdnode/crowdnode.js'; // import * as LocalForageTypes from '../node_modules/localforage/dist/localforage.js'; /** @type {DashTxTypes} */ @@ -69,11 +75,17 @@ export let CrypticStorage = window?.CrypticStorage || globalThis?.CrypticStorage; /** @type {DashWalletTypes} */ export let DashWallet = window?.Wallet || globalThis?.Wallet; +/** @type {CrowdNodeTypes} */ +export let CrowdNode = window?.CrowdNode || globalThis?.CrowdNode export let localforage = window?.localforage || globalThis?.localforage; export default { + Base58Check, + CrowdNode, + Cryptic, + CrypticStorage, DashWallet, DashTx, DashKeys, @@ -81,10 +93,7 @@ export default { DashPhrase, DashSight, DashSocket, - Base58Check, + localforage, RIPEMD160, Secp256k1, - Cryptic, - CrypticStorage, - localforage, }; diff --git a/src/index.html b/src/index.html index 9722a8f..71a1ac2 100644 --- a/src/index.html +++ b/src/index.html @@ -47,6 +47,7 @@ } } + diff --git a/src/rigs/crowdnode-tx.js b/src/rigs/crowdnode-tx.js index 66b86c4..6f29677 100644 --- a/src/rigs/crowdnode-tx.js +++ b/src/rigs/crowdnode-tx.js @@ -12,10 +12,6 @@ import { DialogContructor } from '../components/modal.js' export const crowdnodeTransactionRig = (() => { 'use strict'; - // let { - // mainApp, setupDialog, - // } = globals - let dialogConfig = { state: { name: 'CrowdNode Deposit / Withdraw', @@ -40,13 +36,12 @@ export const crowdnodeTransactionRig = (() => { }, markup: {}, events: {}, - // appElement: mainApp, appElement: document.body, } let crowdnodeTransaction = new DialogContructor(dialogConfig) - console.log('Modal.js Dialog', crowdnodeTransaction) + // console.log('Modal.js Dialog', crowdnodeTransaction) dialogConfig.events.submit = async function (event) { event.preventDefault() @@ -55,77 +50,32 @@ export const crowdnodeTransactionRig = (() => { let fde = formDataEntries(event) if (fde?.intent === 'act') { - // state.elements.dialog.returnValue = String(fde.intent) - let res = await this.state.value.callback?.(this.state.value, fde) + let res = await crowdnodeTransaction.state.value.callback?.( + crowdnodeTransaction.state, + fde, + ) + + // console.log('crowdnodeTransaction submit res', res) if ( res.state.value.status === DIALOG_STATUS.SUCCESS || res.state.value.status === DIALOG_STATUS.ERROR ) { - // this.elements.dialog?.querySelector('progress')?.remove() - this.elements.progress?.remove?.() + crowdnodeTransaction.elements.progress?.remove?.() - this.elements.dialog?.close(fde.intent) + crowdnodeTransaction.elements.dialog.returnValue = String(fde.intent) + crowdnodeTransaction.elements.dialog?.close(String(fde.intent)) } + } else { + crowdnodeTransaction.elements.progress?.remove?.() + + crowdnodeTransaction.elements.dialog.returnValue = 'cancel' + crowdnodeTransaction.elements.dialog?.close('cancel') } } dialogConfig.markup.alert = html`` dialogConfig.markup.submitIcon = `` - // dialogConfig.markup.submitIcon = html` - // - // - // - // ` - // dialogConfig.markup.cancelBtn = html` - // - // ` - // dialogConfig.markup.actionBtn = html` - // - // ` - // dialogConfig.markup.footer = html` - //
- // ${crowdnodeTransaction.markup.alert} - - //
- // ${crowdnodeTransaction.markup.cancelBtn} - // ${crowdnodeTransaction.markup.actionBtn} - //
- //
- // ` - // dialogConfig.markup.fields = html` - //
- // - // Are you sure you want to ${crowdnodeTransaction.state.value.action} ${ - // crowdnodeTransaction.state.value.target || crowdnodeTransaction.state.value.targetFallback - // }? - // - //
- // ` - // dialogConfig.markup.content = () => html` - // ${crowdnodeTransaction.markup.header} - - // ${crowdnodeTransaction.markup.fields} - - // ${crowdnodeTransaction.markup.footer} - // ` crowdnodeTransaction.updateConfig(dialogConfig) diff --git a/src/styles/dialog.css b/src/styles/dialog.css index 25ecf42..5be19e4 100644 --- a/src/styles/dialog.css +++ b/src/styles/dialog.css @@ -181,6 +181,9 @@ dialog > form > fieldset div:has(input + div) { border-radius: 6.25rem; border: 1px solid var(--dark-600); } +dialog > form > fieldset input[type="range"] { + padding: 0; +} dialog > form > fieldset input + p, dialog > form > fieldset label { text-align: left; From d63ca49ba3ace26ad02f8e65ad1ff20079c8ea49 Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Fri, 20 Sep 2024 04:42:33 -0600 Subject: [PATCH 04/12] refactor: :fire: cleanup modal.js --- src/components/modal.js | 174 ++++------------------------------------ 1 file changed, 16 insertions(+), 158 deletions(-) diff --git a/src/components/modal.js b/src/components/modal.js index 781e7a5..561e1f7 100644 --- a/src/components/modal.js +++ b/src/components/modal.js @@ -143,41 +143,11 @@ export const DialogContructor = (() => { this.events = { input: event => { - // let { - // // @ts-ignore - // name: fieldName, form, - // } = event?.target - - // console.log('handle input', { - // fieldName, - // form, - // }) - - if ( - event?.target?.validity?.patternMismatch && - event?.target?.type !== 'checkbox' - ) { - event.preventDefault() - let label = event.target?.previousElementSibling?.textContent?.trim() - if (label) { - event.target.setCustomValidity(`Invalid ${label}`) - } - } else { - event.target.setCustomValidity('') - } - event.target.reportValidity() }, change: event => { - // let { - // // @ts-ignore - // name: fieldName, form, - // } = event?.target - - // console.log('handle change', { - // fieldName, - // form, - // }) - + }, + blur: event => { + // event.preventDefault() if ( event?.target?.validity?.patternMismatch && event?.target?.type !== 'checkbox' @@ -192,26 +162,11 @@ export const DialogContructor = (() => { } event.target.reportValidity() }, - blur: event => { - // event.preventDefault() - // console.log( - // 'handle blur', - // event, - // ) - }, focusout: event => { // event.preventDefault() - // console.log( - // 'handle focus out', - // event, - // ) }, focusin: event => { // event.preventDefault() - // console.log( - // 'handle focus in', - // event, - // ) }, drop: event => { event.preventDefault() @@ -228,19 +183,10 @@ export const DialogContructor = (() => { render: ( state, ) => { - // console.log( - // 'handle dialog render', - // state, - // ) }, show: ( state, ) => { - // console.log( - // 'handle dialog show', - // state, - // ) - // focus first input this.elements.form.querySelector( 'input' @@ -253,12 +199,6 @@ export const DialogContructor = (() => { event.preventDefault() removeAllListeners.bind(this) - // console.log( - // 'handle dialog close', - // event, - // event.target === this.elements.dialog, - // this.elements.dialog.returnValue - // ) this.state.value.status = DIALOG_STATUS.NOT_LOADING this.elements.dialog?.querySelector('progress')?.remove() @@ -267,21 +207,10 @@ export const DialogContructor = (() => { } else { resolve('cancel') } - // console.log( - // 'DIALOG handleClose', - // modal.rendered[this.slugs.dialog], - // ) setTimeout(t => { - // modal.rendered[this.slugs.dialog] = null this.state.value.rendered = null event?.target?.remove() - // console.log( - // 'DIALOG handleClose setTimeout', - // this.state.value.delay, - // // modal.rendered[this.slugs.dialog], - // modal.rendered, - // ) }, this.state.value.delay) }, submit: event => { @@ -291,11 +220,6 @@ export const DialogContructor = (() => { this.elements.dialog.returnValue = String(fde.intent) - // console.log( - // 'handleSubmit', - // [event], - // ) - this.elements.dialog.close(String(fde.intent)) }, reset: event => { @@ -304,19 +228,10 @@ export const DialogContructor = (() => { 'close', this.events.reset ) - // console.log( - // 'handleReset', - // [event.target], - // ) this.elements.dialog.close('cancel') }, click: event => { if (event.target === this.elements.dialog) { - // console.log( - // 'handle dialog backdrop click', - // event, - // event.target === this.elements.dialog - // ) this.elements.dialog.close('cancel') } }, @@ -330,7 +245,6 @@ export const DialogContructor = (() => { this.elements.form = formElement this.elements.progress = progressElement - this.element = this.elements.dialog progressElement.classList.add('pending') @@ -344,7 +258,6 @@ export const DialogContructor = (() => { formElement.name = `${this.slugs.form}` formElement.method = 'dialog' - // formElement.innerHTML = this.markup.content formElement.innerHTML = this.markup.content() dialogElement.insertAdjacentElement( @@ -360,16 +273,12 @@ export const DialogContructor = (() => { removeAllListeners.call(this) addListeners.call(this, resolve, reject) console.log('modal.js dialog show', this.elements.dialog) - // if ( - // !this.state.value.rendered && - // !el.contains(this.elements.dialog) - // ) { - this.render({ - el, - // cfg: config, - position: 'afterend' - }) - // } + + this.render({ + el, + position: 'afterend' + }) + this.elements.dialog.show() this.events.show?.(this) callback?.() @@ -383,16 +292,13 @@ export const DialogContructor = (() => { removeAllListeners.call(this) addListeners.call(this, resolve, reject) console.log('modal.js dialog showModal', this, this.elements.dialog) - // if ( - // !this.state.value.rendered && - // !el.contains(this.elements.dialog) - // ) { - this.render({ - el, - // cfg: config, - position: 'afterend' - }) - // } + + this.render({ + el, + // cfg: config, + position: 'afterend' + }) + this.elements.dialog.showModal() this.events.show?.(this) callback?.() @@ -412,12 +318,6 @@ export const DialogContructor = (() => { console.log('Dialog updateConfig TOP', config) for (let param in config) { - // if (param === 'markup') { - // this[param].value = { - // ...this[param].value, - // ...(config[param] || {}), - // } - // } else { if ('value' in this[param]) { this[param].value = { ...this[param].value, @@ -429,7 +329,6 @@ export const DialogContructor = (() => { ...(config[param] || {}), } } - // } } console.log('Dialog updateConfig BOT', this) @@ -446,11 +345,6 @@ export const DialogContructor = (() => { }) => { console.log('dialog render', this, dl) - // this.elements.form?.removeEventListener?.( - // 'submit', - // this.events.submit, - // ) - if (el !== this.appElement) { this.appElement = el } @@ -461,14 +355,8 @@ export const DialogContructor = (() => { this.elements.dialog.id = this.slugs.dialog this.elements.form.name = this.slugs.form - // this.elements.form.innerHTML = this.markup.content this.elements.form.innerHTML = this.markup.content() - // this.elements.form.addEventListener( - // 'submit', - // this.events.submit, - // ) - console.log('DIALOG RENDER STATE', this.state.value, cfg) console.log('DIALOG RENDER', position, this.slugs.dialog) @@ -480,18 +368,12 @@ export const DialogContructor = (() => { 'beforebegin', this.elements.progress, ) - - // document.body.insertAdjacentHTML( - // 'afterbegin', - // ``, - // ) } if ( this.state.value.status === DIALOG_STATUS.SUCCESS || this.state.value.status === DIALOG_STATUS.ERROR ) { - // document.getElementById('pageLoader')?.remove() this.elements.dialog.querySelector('progress')?.remove() } @@ -507,37 +389,13 @@ export const DialogContructor = (() => { ) } - // console.log('DIALOG RENDER', state, position, state.slugs.dialog, modal.rendered) - this.events.render(this.state) return this } - - // const updateSelf = (cfg, methods = []) => { - // for (let mthd of methods) { - // this[mthd] = { - // ...this[mthd], - // ...(cfg[mthd] || {}), - // } - // } - // } - // updateSelf(cfg, [ - // 'state', 'slugs', 'events', 'elements', 'templates' - // ]) - - // state.render = render } return dl })(); -// export function Modal(config) { -// let dialog = new DialogContructor(config) - -// console.log('Modal.js Dialog', dialog) - -// return dialog -// } - export default DialogContructor From 4e1dbd8ec8d44f0ae90d99ce01e3fb13c0b0072a Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Fri, 20 Sep 2024 04:43:45 -0600 Subject: [PATCH 05/12] refactor: :recycle: cleanup utils.js & remove unused path-to-regexp.js --- src/helpers/utils.js | 44 ++-- src/libs/path-to-regexp.js | 490 ------------------------------------- 2 files changed, 19 insertions(+), 515 deletions(-) delete mode 100644 src/libs/path-to-regexp.js diff --git a/src/helpers/utils.js b/src/helpers/utils.js index f613de1..2730418 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -490,15 +490,7 @@ export function createSignal(initialValue) { return { get value() { - if ( - currentSignal //&& - // currentSignal !== globalDerivedValue - ) { - console.log( - 'currentSignal === derived', - {currentSignal, derived, globalDerivedValue}, - currentSignal === globalDerivedValue, - ) + if (currentSignal) { on(currentSignal) } return _value; @@ -712,22 +704,24 @@ export function DashURLSearchParams(params) { let searchParams let qry = {} - Object.defineProperty(this, "entries", { - enumerable: false, - configurable: false, - writable: false, - value: () => Object.entries(qry), - }); - Object.defineProperty(this, "toString", { - enumerable: false, - configurable: false, - writable: false, - value: () => this.entries().map(p => p.join('=')).join('&'), - }); - Object.defineProperty(this, "size", { - get() { return this.entries().length }, - enumerable: false, - configurable: false, + Object.defineProperties(this, { + entries: { + enumerable: false, + configurable: false, + writable: false, + value: () => Object.entries(qry), + }, + toString: { + enumerable: false, + configurable: false, + writable: false, + value: () => this.entries().map(p => p.join('=')).join('&'), + }, + size: { + enumerable: false, + configurable: false, + get() { return this.entries().length }, + }, }); if (typeof params === 'string' && params !== '') { diff --git a/src/libs/path-to-regexp.js b/src/libs/path-to-regexp.js deleted file mode 100644 index 6394b64..0000000 --- a/src/libs/path-to-regexp.js +++ /dev/null @@ -1,490 +0,0 @@ -// @ts-nocheck - -/** - * https://unpkg.com/path-to-regexp@6.2.1/dist/index.js - * https://github.com/pillarjs/path-to-regexp - * https://github.com/expressjs/express/blob/master/lib/router/layer.js - */ - -/** - * Tokenize input string. - */ -function lexer(str) { - var tokens = []; - var i = 0; - while (i < str.length) { - var char = str[i]; - if (char === '*' || char === '+' || char === '?') { - tokens.push({ type: 'MODIFIER', index: i, value: str[i++] }); - continue; - } - if (char === '\\') { - tokens.push({ type: 'ESCAPED_CHAR', index: i++, value: str[i++] }); - continue; - } - if (char === '{') { - tokens.push({ type: 'OPEN', index: i, value: str[i++] }); - continue; - } - if (char === '}') { - tokens.push({ type: 'CLOSE', index: i, value: str[i++] }); - continue; - } - if (char === ':') { - var name = ''; - var j = i + 1; - while (j < str.length) { - var code = str.charCodeAt(j); - if ( - // `0-9` - (code >= 48 && code <= 57) || - // `A-Z` - (code >= 65 && code <= 90) || - // `a-z` - (code >= 97 && code <= 122) || - // `_` - code === 95 - ) { - name += str[j++]; - continue; - } - break; - } - if (!name) throw new TypeError('Missing parameter name at '.concat(i)); - tokens.push({ type: 'NAME', index: i, value: name }); - i = j; - continue; - } - if (char === '(') { - var count = 1; - var pattern = ''; - var j = i + 1; - if (str[j] === '?') { - throw new TypeError('Pattern cannot start with "?" at '.concat(j)); - } - while (j < str.length) { - if (str[j] === '\\') { - pattern += str[j++] + str[j++]; - continue; - } - if (str[j] === ')') { - count--; - if (count === 0) { - j++; - break; - } - } else if (str[j] === '(') { - count++; - if (str[j + 1] !== '?') { - throw new TypeError( - 'Capturing groups are not allowed at '.concat(j), - ); - } - } - pattern += str[j++]; - } - if (count) throw new TypeError('Unbalanced pattern at '.concat(i)); - if (!pattern) throw new TypeError('Missing pattern at '.concat(i)); - tokens.push({ type: 'PATTERN', index: i, value: pattern }); - i = j; - continue; - } - tokens.push({ type: 'CHAR', index: i, value: str[i++] }); - } - tokens.push({ type: 'END', index: i, value: '' }); - return tokens; -} - -/** - * Parse a string for the raw tokens. - */ -export function parse(str, options) { - if (options === void 0) { - options = {}; - } - var tokens = lexer(str); - var _a = options.prefixes, - prefixes = _a === void 0 ? './' : _a; - var defaultPattern = '[^'.concat( - escapeString(options.delimiter || '/#?'), - ']+?', - ); - var result = []; - var key = 0; - var i = 0; - var path = ''; - var tryConsume = function (type) { - if (i < tokens.length && tokens[i].type === type) return tokens[i++].value; - }; - var mustConsume = function (type) { - var value = tryConsume(type); - if (value !== undefined) return value; - var _a = tokens[i], - nextType = _a.type, - index = _a.index; - throw new TypeError( - 'Unexpected ' - .concat(nextType, ' at ') - .concat(index, ', expected ') - .concat(type), - ); - }; - var consumeText = function () { - var result = ''; - var value; - while ((value = tryConsume('CHAR') || tryConsume('ESCAPED_CHAR'))) { - result += value; - } - return result; - }; - while (i < tokens.length) { - var char = tryConsume('CHAR'); - var name = tryConsume('NAME'); - var pattern = tryConsume('PATTERN'); - if (name || pattern) { - var prefix = char || ''; - if (prefixes.indexOf(prefix) === -1) { - path += prefix; - prefix = ''; - } - if (path) { - result.push(path); - path = ''; - } - result.push({ - name: name || key++, - prefix: prefix, - suffix: '', - pattern: pattern || defaultPattern, - modifier: tryConsume('MODIFIER') || '', - }); - continue; - } - var value = char || tryConsume('ESCAPED_CHAR'); - if (value) { - path += value; - continue; - } - if (path) { - result.push(path); - path = ''; - } - var open = tryConsume('OPEN'); - if (open) { - var prefix = consumeText(); - var name_1 = tryConsume('NAME') || ''; - var pattern_1 = tryConsume('PATTERN') || ''; - var suffix = consumeText(); - mustConsume('CLOSE'); - result.push({ - name: name_1 || (pattern_1 ? key++ : ''), - pattern: name_1 && !pattern_1 ? defaultPattern : pattern_1, - prefix: prefix, - suffix: suffix, - modifier: tryConsume('MODIFIER') || '', - }); - continue; - } - mustConsume('END'); - } - return result; -} - -/** - * Compile a string to a template function for the path. - */ -export function compile(str, options) { - return tokensToFunction(parse(str, options), options); -} - -/** - * Expose a method for transforming tokens into the path function. - */ -export function tokensToFunction(tokens, options) { - if (options === void 0) { - options = {}; - } - var reFlags = flags(options); - var _a = options.encode, - encode = - _a === void 0 - ? function (x) { - return x; - } - : _a, - _b = options.validate, - validate = _b === void 0 ? true : _b; - // Compile all the tokens into regexps. - var matches = tokens.map(function (token) { - if (typeof token === 'object') { - return new RegExp('^(?:'.concat(token.pattern, ')$'), reFlags); - } - }); - return function (data) { - var path = ''; - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i]; - if (typeof token === 'string') { - path += token; - continue; - } - var value = data ? data[token.name] : undefined; - var optional = token.modifier === '?' || token.modifier === '*'; - var repeat = token.modifier === '*' || token.modifier === '+'; - if (Array.isArray(value)) { - if (!repeat) { - throw new TypeError( - 'Expected "'.concat( - token.name, - '" to not repeat, but got an array', - ), - ); - } - if (value.length === 0) { - if (optional) continue; - throw new TypeError( - 'Expected "'.concat(token.name, '" to not be empty'), - ); - } - for (var j = 0; j < value.length; j++) { - var segment = encode(value[j], token); - if (validate && !matches[i].test(segment)) { - throw new TypeError( - 'Expected all "' - .concat(token.name, '" to match "') - .concat(token.pattern, '", but got "') - .concat(segment, '"'), - ); - } - path += token.prefix + segment + token.suffix; - } - continue; - } - if (typeof value === 'string' || typeof value === 'number') { - var segment = encode(String(value), token); - if (validate && !matches[i].test(segment)) { - throw new TypeError( - 'Expected "' - .concat(token.name, '" to match "') - .concat(token.pattern, '", but got "') - .concat(segment, '"'), - ); - } - path += token.prefix + segment + token.suffix; - continue; - } - if (optional) continue; - var typeOfMessage = repeat ? 'an array' : 'a string'; - throw new TypeError( - 'Expected "'.concat(token.name, '" to be ').concat(typeOfMessage), - ); - } - return path; - }; -} - -/** - * Create path match function from `path-to-regexp` spec. - */ -export function match(str, options) { - var keys = []; - var re = pathToRegexp(str, keys, options); - return regexpToFunction(re, keys, options); -} - -/** - * Create a path match function from `path-to-regexp` output. - */ -export function regexpToFunction(re, keys, options) { - if (options === void 0) { - options = {}; - } - var _a = options.decode, - decode = - _a === void 0 - ? function (x) { - return x; - } - : _a; - return function (pathname) { - var m = re.exec(pathname); - if (!m) return false; - var path = m[0], - index = m.index; - var params = Object.create(null); - var _loop_1 = function (i) { - if (m[i] === undefined) return 'continue'; - var key = keys[i - 1]; - if (key.modifier === '*' || key.modifier === '+') { - params[key.name] = m[i] - .split(key.prefix + key.suffix) - .map(function (value) { - return decode(value, key); - }); - } else { - params[key.name] = decode(m[i], key); - } - }; - for (var i = 1; i < m.length; i++) { - _loop_1(i); - } - return { path: path, index: index, params: params }; - }; -} - -/** - * Escape a regular expression string. - */ -function escapeString(str) { - return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1'); -} - -/** - * Get the flags for a regexp from the options. - */ -function flags(options) { - return options && options.sensitive ? '' : 'i'; -} - -/** - * Pull out keys from a regexp. - */ -function regexpToRegexp(path, keys) { - if (!keys) return path; - var groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g; - var index = 0; - var execResult = groupsRegex.exec(path.source); - while (execResult) { - keys.push({ - // Use parenthesized substring match if available, index otherwise - name: execResult[1] || index++, - prefix: '', - suffix: '', - modifier: '', - pattern: '', - }); - execResult = groupsRegex.exec(path.source); - } - return path; -} - -/** - * Transform an array into a regexp. - */ -function arrayToRegexp(paths, keys, options) { - var parts = paths.map(function (path) { - return pathToRegexp(path, keys, options).source; - }); - return new RegExp('(?:'.concat(parts.join('|'), ')'), flags(options)); -} - -/** - * Create a path regexp from string input. - */ -function stringToRegexp(path, keys, options) { - return tokensToRegexp(parse(path, options), keys, options); -} - -/** - * Expose a function for taking tokens and returning a RegExp. - */ -export function tokensToRegexp(tokens, keys, options) { - if (options === void 0) { - options = {}; - } - var _a = options.strict, - strict = _a === void 0 ? false : _a, - _b = options.start, - start = _b === void 0 ? true : _b, - _c = options.end, - end = _c === void 0 ? true : _c, - _d = options.encode, - encode = - _d === void 0 - ? function (x) { - return x; - } - : _d, - _e = options.delimiter, - delimiter = _e === void 0 ? '/#?' : _e, - _f = options.endsWith, - endsWith = _f === void 0 ? '' : _f; - var endsWithRe = '['.concat(escapeString(endsWith), ']|$'); - var delimiterRe = '['.concat(escapeString(delimiter), ']'); - var route = start ? '^' : ''; - // Iterate over the tokens and create our regexp string. - for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) { - var token = tokens_1[_i]; - if (typeof token === 'string') { - route += escapeString(encode(token)); - } else { - var prefix = escapeString(encode(token.prefix)); - var suffix = escapeString(encode(token.suffix)); - if (token.pattern) { - if (keys) keys.push(token); - if (prefix || suffix) { - if (token.modifier === '+' || token.modifier === '*') { - var mod = token.modifier === '*' ? '?' : ''; - route += '(?:' - .concat(prefix, '((?:') - .concat(token.pattern, ')(?:') - .concat(suffix) - .concat(prefix, '(?:') - .concat(token.pattern, '))*)') - .concat(suffix, ')') - .concat(mod); - } else { - route += '(?:' - .concat(prefix, '(') - .concat(token.pattern, ')') - .concat(suffix, ')') - .concat(token.modifier); - } - } else { - if (token.modifier === '+' || token.modifier === '*') { - route += '((?:' - .concat(token.pattern, ')') - .concat(token.modifier, ')'); - } else { - route += '('.concat(token.pattern, ')').concat(token.modifier); - } - } - } else { - route += '(?:' - .concat(prefix) - .concat(suffix, ')') - .concat(token.modifier); - } - } - } - if (end) { - if (!strict) route += ''.concat(delimiterRe, '?'); - route += !options.endsWith ? '$' : '(?='.concat(endsWithRe, ')'); - } else { - var endToken = tokens[tokens.length - 1]; - var isEndDelimited = - typeof endToken === 'string' - ? delimiterRe.indexOf(endToken[endToken.length - 1]) > -1 - : endToken === undefined; - if (!strict) { - route += '(?:'.concat(delimiterRe, '(?=').concat(endsWithRe, '))?'); - } - if (!isEndDelimited) { - route += '(?='.concat(delimiterRe, '|').concat(endsWithRe, ')'); - } - } - return new RegExp(route, flags(options)); -} - -/** - * Normalize the given path string, returning a regular expression. - * - * An empty array can be passed in for the keys, which will hold the - * placeholder key descriptions. For example, using `/user/:id`, `keys` will - * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. - */ -export function pathToRegexp(path, keys, options) { - if (path instanceof RegExp) return regexpToRegexp(path, keys); - if (Array.isArray(path)) return arrayToRegexp(path, keys, options); - return stringToRegexp(path, keys, options); -} From 3fad608ec023536d73b68d3a52fc91d062681a1d Mon Sep 17 00:00:00 2001 From: jojobyte <184880+jojobyte@users.noreply.github.com> Date: Fri, 20 Sep 2024 04:46:50 -0600 Subject: [PATCH 06/12] refactor: :recycle: cleanup & sync CN withdraw dialog fields --- src/main.js | 138 +++++++++++++++++++++------------------------------- 1 file changed, 55 insertions(+), 83 deletions(-) diff --git a/src/main.js b/src/main.js index f23ab8e..5b3bd81 100644 --- a/src/main.js +++ b/src/main.js @@ -139,6 +139,7 @@ let contactsList = await setupContactsList( } let contactAccountID = Object.values(contactData.incoming || {})?.[0]?.accountIndex + console.log('contact click data', contactData) let shareAccount = await deriveWalletData( @@ -812,13 +813,6 @@ async function main() { }) import('./components/crowdnode-card.js') .then(async ({ CrowdNodeCard }) => { - // let crowdnodeTransaction = crowdnodeTransactionRig - console.log( - 'crowdnodeTransactionRig', - crowdnodeTransactionRig, - crowdnodeTransactionRig.markup.fields, - ) - // crowdnodeTransactionRig.markup.fields let cfg = { state: { // name: '', @@ -885,17 +879,10 @@ async function main() { name: 'CrowdNode Funding', amount: 1.1, status: DIALOG_STATUS.LOADING, - // wallet, - // contacts: appState.contacts, - // submitTxt: `Fund your wallet`, - // submitAlt: `Change the currently selected contact`, - generateNextAddress: state => { - // return html`` - return html` - Send 1.1 Dash or more
- to signup & fund your CrowdNode account. - ` - }, + generateNextAddress: state => html` + Send 1.1 Dash or more
+ to signup & fund your CrowdNode account. + `, footer: state => html`