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..c988227b 100644
--- a/src/Create.jsx
+++ b/src/Create.jsx
@@ -9,8 +9,10 @@ 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 { isMobile } from "./helper";
import t from "./i18n";
import {
addressValid,
@@ -40,6 +42,7 @@ import {
setSendAmountFormatted,
setSendAmountValid,
setValid,
+ wasmSupported,
webln,
} from "./signals";
import { calculateReceiveAmount, calculateSendAmount } from "./utils/calculate";
@@ -293,6 +296,9 @@ const Create = () => {
+
+
+
diff --git a/src/components/AddressInput.tsx b/src/components/AddressInput.tsx
index 6d57e96c..9b3bdced 100644
--- a/src/components/AddressInput.tsx
+++ b/src/components/AddressInput.tsx
@@ -1,10 +1,11 @@
-import { createEffect } from "solid-js";
+import { createEffect, on } from "solid-js";
import { decodeAddress } from "../compat";
import { RBTC } from "../consts";
import t from "../i18n";
import {
asset,
+ onchainAddress,
reverse,
sendAmountValid,
setAddressValid,
@@ -39,11 +40,13 @@ const AddressInput = () => {
}
};
- createEffect(() => {
- if (sendAmountValid() && reverse() && asset() !== RBTC) {
- validateAddress(inputRef);
- }
- });
+ createEffect(
+ on([sendAmountValid, onchainAddress], () => {
+ if (reverse() && asset() !== RBTC) {
+ validateAddress(inputRef);
+ }
+ }),
+ );
return (
{
name="onchainAddress"
autocomplete="off"
placeholder={t("onchain_address", { asset: asset() })}
+ value={onchainAddress()}
/>
);
};
diff --git a/src/components/QrScan.tsx b/src/components/QrScan.tsx
new file mode 100644
index 00000000..38b2e583
--- /dev/null
+++ b/src/components/QrScan.tsx
@@ -0,0 +1,86 @@
+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("");
+ setOnchainAddress(result.data);
+ } else {
+ setInvoice("");
+ setInvoice(result.data);
+ }
+ stopScan();
+ },
+ {},
+ );
+ }
+ qrScanner.start();
+ };
+
+ const stopScan = () => {
+ if (scanning()) {
+ qrScanner.destroy();
+ setScanning(false);
+ qrScanner = undefined;
+ }
+ };
+
+ onCleanup(() => {
+ stopScan();
+ });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ X
+
+
+
+
+
+ >
+ );
+};
+
+export default QrScan;
diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts
index 51a68cc2..bae2a50a 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: "中文",
@@ -639,6 +642,7 @@ const dict = {
"按“{{ button }}”以打开已连接的钱包并确认显示的交易。",
fetch_lnurl: "从LNURL/LNAddress获取发票",
invalid_address: "无效的{{ asset }}地址",
+ scan_qr_code: "扫描 QR 码",
},
};
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..365f6ea4
--- /dev/null
+++ b/src/style/qrscan.scss
@@ -0,0 +1,31 @@
+#video-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1;
+}
+
+.close {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ z-index: 2;
+ background: rgba(255, 255, 255, 0.6);
+ cursor: pointer;
+ padding: 5px;
+ border-radius: 50%;
+ cursor: pointer;
+ color: #14283f;
+ font-weight: 800;
+ font-size: 23px;
+ width: 42px;
+}
+
+video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
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");
diff --git a/tests/components/QrScan.spec.tsx b/tests/components/QrScan.spec.tsx
new file mode 100644
index 00000000..4dbce9ba
--- /dev/null
+++ b/tests/components/QrScan.spec.tsx
@@ -0,0 +1,15 @@
+import { render, screen } from "@solidjs/testing-library";
+import { describe, expect, test } 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();
+ });
+});
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: {