From 37927f7eda94a58ece266ccb0a7090edae61a556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sat, 18 Nov 2023 14:27:52 +0100 Subject: [PATCH 01/13] feat: add qrscanner for addresses and invoices qr code scanner for mobile devices! :) qr fixes remove hr --- package-lock.json | 270 +++++++++++++++++++++++++++++++++++--- package.json | 1 + src/Create.jsx | 2 + src/components/QrScan.tsx | 82 ++++++++++++ src/i18n/i18n.ts | 3 + src/signals.js | 4 +- src/style/qrscan.scss | 9 ++ vite.config.js | 4 +- 8 files changed, 355 insertions(+), 20 deletions(-) create mode 100644 src/components/QrScan.tsx create mode 100644 src/style/qrscan.scss diff --git a/package-lock.json b/package-lock.json index c6806920..896568d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "sass": "^1.69.5", "typescript": "^5.2.2", "vite": "^4.5.1", + "vite-plugin-mkcert": "^1.16.0", "vite-plugin-node-polyfills": "^0.15.0", "vite-plugin-solid": "^2.7.2", "vitest": "^0.32.4" @@ -886,6 +887,177 @@ } ] }, + "node_modules/@octokit/auth-token": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", + "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", + "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/endpoint": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", + "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "dev": true, + "dependencies": { + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/graphql": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", + "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "dev": true, + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^9.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", + "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", + "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "dev": true, + "dependencies": { + "@octokit/tsconfig": "^1.0.2", + "@octokit/types": "^9.2.3" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", + "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "dev": true, + "dependencies": { + "@octokit/types": "^10.0.0" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", + "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", + "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^9.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/rest": { + "version": "19.0.13", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", + "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "dev": true, + "dependencies": { + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/tsconfig": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", + "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", + "dev": true + }, + "node_modules/@octokit/types": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", + "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, "node_modules/@openzeppelin/contracts": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.0.tgz", @@ -2102,6 +2274,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-plugin-jsx-dom-expressions": { "version": "0.37.2", "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.37.2.tgz", @@ -2182,6 +2365,12 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true + }, "node_modules/bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", @@ -3286,6 +3475,12 @@ "node": ">=0.4.0" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, "node_modules/des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -3770,6 +3965,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4612,6 +4827,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -5508,8 +5732,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -5529,25 +5751,19 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -5773,8 +5989,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "wrappy": "1" } @@ -6156,9 +6370,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/psl": { "version": "1.9.0", @@ -7387,6 +7599,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -7585,6 +7803,24 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-plugin-mkcert": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/vite-plugin-mkcert/-/vite-plugin-mkcert-1.16.0.tgz", + "integrity": "sha512-5r+g8SB9wZzLNUFekGwZo3e0P6QlS6rbxK5p9z/itxNAimsYohgjK/YfVPVxM9EuglP9hjridq0lUejo9v1nVg==", + "dev": true, + "dependencies": { + "@octokit/rest": "^19.0.5", + "axios": "^1.2.2", + "debug": "^4.3.4", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=v16.7.0" + }, + "peerDependencies": { + "vite": ">=3" + } + }, "node_modules/vite-plugin-node-polyfills": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.15.0.tgz", @@ -8039,9 +8275,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/ws": { "version": "8.13.0", diff --git a/package.json b/package.json index 7829cf48..077bb913 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "sass": "^1.69.5", "typescript": "^5.2.2", "vite": "^4.5.1", + "vite-plugin-mkcert": "^1.16.0", "vite-plugin-node-polyfills": "^0.15.0", "vite-plugin-solid": "^2.7.2", "vitest": "^0.32.4" diff --git a/src/Create.jsx b/src/Create.jsx index 11dc46a7..c35e09aa 100644 --- a/src/Create.jsx +++ b/src/Create.jsx @@ -9,6 +9,7 @@ import ConnectMetamask from "./components/ConnectMetamask"; import { CreateButton, setButtonLabel } from "./components/CreateButton"; import Fees from "./components/Fees"; import InvoiceInput from "./components/InvoiceInput"; +import QrScan from "./components/QrScan"; import Reverse from "./components/Reverse"; import { RBTC, sideReceive, sideSend } from "./consts"; import t from "./i18n"; @@ -293,6 +294,7 @@ const Create = () => { + diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx new file mode 100644 index 00000000..e501e8f6 --- /dev/null +++ b/src/components/QrScan.tsx @@ -0,0 +1,82 @@ +import log from "loglevel"; +import QrScanner from "qr-scanner"; +import { Show, createSignal, onCleanup, onMount } from "solid-js"; + +import t from "../i18n"; +import { + camera, + reverse, + setCamera, + setInvoice, + setOnchainAddress, +} from "../signals"; +import "../style/qrscan.scss"; + +const QrScan = () => { + let qrRef: HTMLVideoElement; + let qrScanner: QrScanner; + + const [scanning, setScanning] = createSignal(false); + + onMount(async () => { + const hasCamera = await QrScanner.hasCamera(); + log.debug("detecting camera: ", hasCamera); + if (!hasCamera) { + return; + } + setCamera(hasCamera); + }); + + const startScan = () => { + setScanning(true); + if (qrScanner === undefined) { + qrScanner = new QrScanner( + qrRef, + (result) => { + log.debug("scanned qr code:", result.data); + if (reverse()) { + setOnchainAddress(result.data); + } else { + setInvoice(result.data); + } + stopScan(); + }, + {}, + ); + } + qrScanner.start(); + }; + + const stopScan = () => { + if (scanning()) { + qrScanner.destroy(); + setScanning(false); + qrScanner = undefined; + } + }; + + onCleanup(() => { + stopScan(); + }); + + return ( + <> + + + + + + + +
+
+ + ); +}; + +export default QrScan; diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 51a68cc2..09624853 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -157,6 +157,7 @@ const dict = { 'Press "{{ button }}" in order to open your connected wallet and confirm the displayed transaction.', fetch_lnurl: "Fetch invoice from LNURL/LNAddress", invalid_address: "Invalid {{ asset }} address", + scan_qr_code: "Scan QR Code", }, de: { language: "Deutsch", @@ -323,6 +324,7 @@ const dict = { '"{{ button }}" klicken um das verbundene Wallet zu öffnen und bestätige die angezeigte Transaktion.', fetch_lnurl: "Hole Rechnung von LNURL/LNAdresse", invalid_address: "Ungültige {{ asset }} Adresse", + scan_qr_code: "QR Code scannen", }, es: { language: "Español", @@ -488,6 +490,7 @@ const dict = { 'Pulse "{{ button }}" para abrir su monedero conectado y confirmar la transacción mostrada.', fetch_lnurl: "Obtener factura de LNURL/LNAddress", invalid_address: "Dirección {{ asset }} inválida", + scan_qr_code: "Escanear código QR", }, zh: { language: "中文", diff --git a/src/signals.js b/src/signals.js index f441f4c9..2b227b5d 100644 --- a/src/signals.js +++ b/src/signals.js @@ -17,7 +17,6 @@ export const [config, setConfig] = createSignal(0); export const [online, setOnline] = createSignal(true); export const [wasmSupported, setWasmSupported] = createSignal(true); -export const [webln, setWebln] = createSignal(false); // fees export const [boltzFee, setBoltzFee] = createSignal(0); @@ -106,6 +105,9 @@ export const [sendAmountValid, setSendAmountValid] = createSignal(true); export const [notification, setNotification] = createSignal(""); export const [notificationType, setNotificationType] = createSignal(""); +export const [webln, setWebln] = createSignal(false); +export const [camera, setCamera] = createSignal(false); + // effects createRoot(() => { createEffect(() => setReverse(assetReceive() !== LN)); diff --git a/src/style/qrscan.scss b/src/style/qrscan.scss new file mode 100644 index 00000000..a168eef0 --- /dev/null +++ b/src/style/qrscan.scss @@ -0,0 +1,9 @@ +video { + width: 100%; + height: 100%; + object-fit: cover; + position: absolute; + top: 0; + left: 0; + z-index: 1; +} diff --git a/vite.config.js b/vite.config.js index e9318f8c..cc899a5f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,10 +1,12 @@ import { defineConfig } from "vite"; import solidPlugin from "vite-plugin-solid"; import { nodePolyfills } from "vite-plugin-node-polyfills"; +import mkcert from "vite-plugin-mkcert"; export default defineConfig({ - plugins: [solidPlugin(), nodePolyfills()], + plugins: [solidPlugin(), nodePolyfills(), mkcert()], server: { + https: true, cors: { origin: "*" }, }, build: { From 4fdd310527fe2bf10be48f5c54c7c5846e717934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 7 Dec 2023 08:49:08 +0100 Subject: [PATCH 02/13] add test --- tests/components/QrScan.spec.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/components/QrScan.spec.tsx diff --git a/tests/components/QrScan.spec.tsx b/tests/components/QrScan.spec.tsx new file mode 100644 index 00000000..09e4101c --- /dev/null +++ b/tests/components/QrScan.spec.tsx @@ -0,0 +1,15 @@ +import { render, screen } from "@solidjs/testing-library"; +import { describe, expect, test, vi } from "vitest"; + +import QrScan from "../../src/components/QrScan"; +import t from "../../src/i18n"; +import * as signals from "../../src/signals"; + +describe("QrScan", () => { + test("should render the QrScan component", async () => { + render(() => ); + signals.setCamera(true); + const button = await screen.findByText(t("scan_qr_code")); + expect(button).not.toBeUndefined(); + }); +}); From c1661074767271e98a46c93947b86d28f2f56dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 7 Dec 2023 09:32:16 +0100 Subject: [PATCH 03/13] fixup! --- tests/components/QrScan.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/QrScan.spec.tsx b/tests/components/QrScan.spec.tsx index 09e4101c..4dbce9ba 100644 --- a/tests/components/QrScan.spec.tsx +++ b/tests/components/QrScan.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from "@solidjs/testing-library"; -import { describe, expect, test, vi } from "vitest"; +import { describe, expect, test } from "vitest"; import QrScan from "../../src/components/QrScan"; import t from "../../src/i18n"; From fa139351475c52356d9500197021836e44e2a3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 11 Dec 2023 13:40:02 +0100 Subject: [PATCH 04/13] only show on mobile --- src/components/QrScan.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx index e501e8f6..3b7de27b 100644 --- a/src/components/QrScan.tsx +++ b/src/components/QrScan.tsx @@ -2,6 +2,7 @@ import log from "loglevel"; import QrScanner from "qr-scanner"; import { Show, createSignal, onCleanup, onMount } from "solid-js"; +import { isMobile } from "../helper"; import t from "../i18n"; import { camera, @@ -24,7 +25,7 @@ const QrScan = () => { if (!hasCamera) { return; } - setCamera(hasCamera); + setCamera(hasCamera && isMobile); }); const startScan = () => { From 7220467c72c4ca568168fb43c524786816fc255e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 13 Dec 2023 20:47:14 +0100 Subject: [PATCH 05/13] onchainaddress --- src/components/AddressInput.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/AddressInput.tsx b/src/components/AddressInput.tsx index 6d57e96c..e14b51b9 100644 --- a/src/components/AddressInput.tsx +++ b/src/components/AddressInput.tsx @@ -5,6 +5,7 @@ import { RBTC } from "../consts"; import t from "../i18n"; import { asset, + onchainAddress, reverse, sendAmountValid, setAddressValid, @@ -57,6 +58,7 @@ const AddressInput = () => { name="onchainAddress" autocomplete="off" placeholder={t("onchain_address", { asset: asset() })} + value={onchainAddress()} /> ); }; From d9e0f604e59deae6003bba0b6e532c7846fb6341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 13 Dec 2023 21:11:34 +0100 Subject: [PATCH 06/13] dont check havebeen called times --- tests/components/AddressInput.spec.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/components/AddressInput.spec.tsx b/tests/components/AddressInput.spec.tsx index f67e2b66..d46bbe1a 100644 --- a/tests/components/AddressInput.spec.tsx +++ b/tests/components/AddressInput.spec.tsx @@ -39,11 +39,9 @@ describe("AddressInput", () => { target: { value: address }, }); - expect(setAddressValid).toHaveBeenCalledTimes(2); expect(setAddressValid).toHaveBeenCalledWith(valid); if (valid) { - expect(setOnchainAddress).toHaveBeenCalledTimes(1); expect(setOnchainAddress).toHaveBeenCalledWith(address); } else { expect(input.className).toEqual("invalid"); From 889cf94b7580a209c48748e7db158953e1574b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 13 Dec 2023 21:40:05 +0100 Subject: [PATCH 07/13] fixup! --- src/components/AddressInput.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/AddressInput.tsx b/src/components/AddressInput.tsx index e14b51b9..9b3bdced 100644 --- a/src/components/AddressInput.tsx +++ b/src/components/AddressInput.tsx @@ -1,4 +1,4 @@ -import { createEffect } from "solid-js"; +import { createEffect, on } from "solid-js"; import { decodeAddress } from "../compat"; import { RBTC } from "../consts"; @@ -40,11 +40,13 @@ const AddressInput = () => { } }; - createEffect(() => { - if (sendAmountValid() && reverse() && asset() !== RBTC) { - validateAddress(inputRef); - } - }); + createEffect( + on([sendAmountValid, onchainAddress], () => { + if (reverse() && asset() !== RBTC) { + validateAddress(inputRef); + } + }), + ); return ( Date: Tue, 19 Dec 2023 09:13:07 +0100 Subject: [PATCH 08/13] check ismobile outside of component --- src/Create.jsx | 5 ++++- src/components/QrScan.tsx | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Create.jsx b/src/Create.jsx index c35e09aa..5f497e36 100644 --- a/src/Create.jsx +++ b/src/Create.jsx @@ -12,6 +12,7 @@ import InvoiceInput from "./components/InvoiceInput"; import QrScan from "./components/QrScan"; import Reverse from "./components/Reverse"; import { RBTC, sideReceive, sideSend } from "./consts"; +import { isMobile } from "./helper"; import t from "./i18n"; import { addressValid, @@ -294,7 +295,9 @@ const Create = () => { - + + + diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx index 3b7de27b..e501e8f6 100644 --- a/src/components/QrScan.tsx +++ b/src/components/QrScan.tsx @@ -2,7 +2,6 @@ import log from "loglevel"; import QrScanner from "qr-scanner"; import { Show, createSignal, onCleanup, onMount } from "solid-js"; -import { isMobile } from "../helper"; import t from "../i18n"; import { camera, @@ -25,7 +24,7 @@ const QrScan = () => { if (!hasCamera) { return; } - setCamera(hasCamera && isMobile); + setCamera(hasCamera); }); const startScan = () => { From 2251dd627a6bcf258f011d73390014958038f783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 19 Dec 2023 09:40:43 +0100 Subject: [PATCH 09/13] reset onchaoin address on scanner --- src/components/QrScan.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx index e501e8f6..71f241a7 100644 --- a/src/components/QrScan.tsx +++ b/src/components/QrScan.tsx @@ -35,8 +35,10 @@ const QrScan = () => { (result) => { log.debug("scanned qr code:", result.data); if (reverse()) { + setOnchainAddress(""); setOnchainAddress(result.data); } else { + setInvoice(""); setInvoice(result.data); } stopScan(); From d0274de2ed37abd4efb20ed76f922fbffc394551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 20 Dec 2023 07:52:59 +0100 Subject: [PATCH 10/13] add close button --- src/components/QrScan.tsx | 10 ++++++---- src/style/qrscan.scss | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx index 71f241a7..38b2e583 100644 --- a/src/components/QrScan.tsx +++ b/src/components/QrScan.tsx @@ -70,10 +70,12 @@ const QrScan = () => { - +
+ + + X + +

diff --git a/src/style/qrscan.scss b/src/style/qrscan.scss index a168eef0..379c3a8e 100644 --- a/src/style/qrscan.scss +++ b/src/style/qrscan.scss @@ -1,4 +1,5 @@ -video { +#video-wrapper { + position: relative; width: 100%; height: 100%; object-fit: cover; @@ -7,3 +8,23 @@ video { left: 0; z-index: 1; } + +.close { + position: absolute; + top: 12px; + right: 12px; + z-index: 2; + background: rgba(255, 255, 255, 0.6); + padding: 5px; + border-radius: 50%; + cursor: pointer; + color: #14283f; + font-weight: 800; + font-size: 23px; + width: 42px; +} + +video { + width: 100%; + height: 100%; +} From 9a3dca9022f020b02244e20b4c37a07df6e3ee9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 20 Dec 2023 07:58:44 +0100 Subject: [PATCH 11/13] fixup! --- src/style/qrscan.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/style/qrscan.scss b/src/style/qrscan.scss index 379c3a8e..365f6ea4 100644 --- a/src/style/qrscan.scss +++ b/src/style/qrscan.scss @@ -2,7 +2,6 @@ position: relative; width: 100%; height: 100%; - object-fit: cover; position: absolute; top: 0; left: 0; @@ -15,6 +14,7 @@ right: 12px; z-index: 2; background: rgba(255, 255, 255, 0.6); + cursor: pointer; padding: 5px; border-radius: 50%; cursor: pointer; @@ -27,4 +27,5 @@ video { width: 100%; height: 100%; + object-fit: cover; } From 9c52b9ad26abd7e7924195e49f5f669c4e2db5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 22 Dec 2023 05:40:19 +0100 Subject: [PATCH 12/13] qrscanner does not work without wasm --- src/Create.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Create.jsx b/src/Create.jsx index 5f497e36..c988227b 100644 --- a/src/Create.jsx +++ b/src/Create.jsx @@ -42,6 +42,7 @@ import { setSendAmountFormatted, setSendAmountValid, setValid, + wasmSupported, webln, } from "./signals"; import { calculateReceiveAmount, calculateSendAmount } from "./utils/calculate"; @@ -295,7 +296,7 @@ const Create = () => { - + From 49fd4870322636e3f126a649a7a63464a5dd9274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 3 Jan 2024 10:55:53 +0100 Subject: [PATCH 13/13] add chinese --- src/i18n/i18n.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 09624853..bae2a50a 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -642,6 +642,7 @@ const dict = { "按“{{ button }}”以打开已连接的钱包并确认显示的交易。", fetch_lnurl: "从LNURL/LNAddress获取发票", invalid_address: "无效的{{ asset }}地址", + scan_qr_code: "扫描 QR 码", }, };