diff --git a/package.json b/package.json index 0e68342..ccb219b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "dependencies": { "@emoji-mart/data": "^1.1.2", "@emoji-mart/react": "^1.1.1", + "@getalby/bitcoin-connect-react": "1.0.0", "@noble/curves": "^1.1.0", "@noble/hashes": "^1.3.1", "@radix-ui/react-collapsible": "^1.0.3", diff --git a/public/icons.svg b/public/icons.svg index a7420d8..11ed637 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -88,5 +88,8 @@ + + + diff --git a/src/element/copy.tsx b/src/element/copy.tsx index 2ccc0ba..92e30f5 100644 --- a/src/element/copy.tsx +++ b/src/element/copy.tsx @@ -5,16 +5,17 @@ import { Icon } from "./icon"; export interface CopyProps { text: string; maxSize?: number; + hideText?: boolean; className?: string; } -export default function Copy({ text, maxSize = 32, className }: CopyProps) { +export default function Copy({ text, maxSize = 32, className, hideText }: CopyProps) { const { copy, copied } = useCopy(); const sliceLength = maxSize / 2; const trimmed = text.length > maxSize ? `${text.slice(0, sliceLength)}...${text.slice(-sliceLength)}` : text; return (
copy(text)}> - {trimmed} + {!hideText && {trimmed}} {copied ? : } diff --git a/src/element/login-signup.tsx b/src/element/login-signup.tsx index 8ce3ab9..adc3dda 100644 --- a/src/element/login-signup.tsx +++ b/src/element/login-signup.tsx @@ -10,8 +10,8 @@ import { Icon } from "./icon"; import Copy from "./copy"; import { hexToBech32, openFile } from "utils"; import { VoidApi } from "@void-cat/api"; -import { LoginType } from "login"; import { FormattedMessage } from "react-intl"; +import { bech32 } from "@scure/base"; enum Stage { Login = 0, @@ -26,13 +26,19 @@ export function LoginSignup({ close }: { close: () => void }) { const [avatar, setAvatar] = useState(""); const [key, setNewKey] = useState(""); - async function doLogin() { + function doLoginNsec() { try { - const pub = await EventPublisher.nip7(); - if (pub) { - Login.loginWithPubkey(pub.pubKey, LoginType.Nip7); - close(); + let nsec = prompt("Enter your nsec\nWARNING: THIS IS NOT RECOMMENDED. DO NOT IMPORT ANY KEYS YOU CARE ABOUT"); + if (!nsec) { + throw new Error("no nsec provided"); } + if (nsec.startsWith("nsec")) { + const {words} = bech32.decode(nsec, 5000); + const data = new Uint8Array(bech32.fromWords(words)); + nsec = bytesToHex(data); + } + Login.loginWithPrivateKey(nsec); + close(); } catch (e) { console.error(e); if (e instanceof Error) { @@ -92,16 +98,26 @@ export function LoginSignup({ close }: { close: () => void }) { return ( <>

- +

- {"nostr" in window && ( - - - - )} - +
+
+ +
+
+ {error && {error}} ); @@ -124,7 +140,7 @@ export function LoginSignup({ close }: { close: () => void }) {
-
+
setUsername(e.target.value)} />
diff --git a/src/index.css b/src/index.css index da04b4a..787fa77 100644 --- a/src/index.css +++ b/src/index.css @@ -17,6 +17,7 @@ body { --text-danger: #ff563f; --surface: #222; --border: #171717; + --border-2: #393939; --gradient-purple: linear-gradient(135deg, #882bff 0%, #f83838 100%); --gradient-yellow: linear-gradient(270deg, #adff27 0%, #ffd027 100%); --gradient-orange: linear-gradient(270deg, #ff5b27 0%, rgba(255, 182, 39, 0.99) 100%); @@ -107,6 +108,11 @@ a { align-items: center; justify-content: center; gap: 8px; + height: 44px; +} + +.btn-block { + width: 100%; } .btn-small { @@ -226,6 +232,7 @@ div.paper { .dialog-content { display: flex; flex-direction: column; + align-items: center; gap: 12px; z-index: 2; background-color: #171717; @@ -235,21 +242,45 @@ div.paper { left: 50%; transform: translate(-50%, -50%); width: 90vw; - max-width: 450px; + max-width: 430px; max-height: 85vh; - padding: 25px; overflow-y: auto; } +.dialog-content .header-image { + width: 100%; + height: auto; +} +.dialog-content .content-inner { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + padding: 25px; + box-sizing: border-box; + gap: 16px; +} + +.dialog-content .username, .dialog-content .username input { + width: 100%; +} .dialog-content div.paper { background: #262626; + width: 100%; + box-sizing: border-box; } -.dialog-content h3 { +.dialog-content h2 { font-size: 24px; font-weight: 500; margin: 0; } +.dialog-content h3 { + font-size: 16px; + font-weight: 500; + margin: 0; + margin-bottom: 24px; +} .dialog-content small { display: block; @@ -304,3 +335,15 @@ div.paper { .secondary { color: #909090; } + +.or-divider { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + gap: 16px; +} +.or-divider hr { + width: 135px; + border-color: var(--border-2); +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index e1b0611..08e313a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -18,6 +18,7 @@ import { LoginStore } from "login"; import { StreamProvidersPage } from "pages/providers"; import { defaultRelays } from "const"; import { CatchAllRoutePage } from "pages/catch-all"; +import { SettingsPage } from "pages/settings-page"; import { register } from "serviceWorker"; import { IntlProvider } from "intl"; import { WidgetsPage } from "pages/widgets"; @@ -67,6 +68,10 @@ const router = createBrowserRouter([ path: "/providers/:id?", element: , }, + { + path: "/settings", + element: , + }, { path: "/widgets", element: , diff --git a/src/login-header.png b/src/login-header.png new file mode 100644 index 0000000..61220d1 Binary files /dev/null and b/src/login-header.png differ diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index e440abc..bb90c4d 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -13,6 +13,9 @@ import { Menu, MenuItem } from "@szhsin/react-menu"; import { hexToBech32 } from "@snort/shared"; import { Login } from "index"; import { FormattedMessage } from "react-intl"; +import { EventPublisher } from "@snort/system"; +import { LoginType } from "login"; +import LoginHeader from "../login-header.png"; export function LayoutPage() { const navigate = useNavigate(); @@ -46,6 +49,10 @@ export function LayoutPage() { + navigate("/settings")}> + + Settings + Login.logout()}> @@ -58,18 +65,33 @@ export function LayoutPage() { function loggedOut() { if (login) return; + async function handleLogin() { + try { + const pub = await EventPublisher.nip7(); + if (pub) { + Login.loginWithPubkey(pub.pubKey, LoginType.Nip7); + return; + } + } + catch(e) { + console.error(e); + } + setShowLogin(true); + } + return ( - - - + - setShowLogin(false)} /> + +
+ setShowLogin(false)} /> +
diff --git a/src/pages/settings-page.css b/src/pages/settings-page.css new file mode 100644 index 0000000..d2e8f74 --- /dev/null +++ b/src/pages/settings-page.css @@ -0,0 +1,16 @@ +.profile-page { + display: flex; + justify-content: center; +} + +.public-key { + display: flex; + align-items: center; + gap: 8px; +} + +.private-key { + display: flex; + align-items: center; + gap: 8px; +} diff --git a/src/pages/settings-page.tsx b/src/pages/settings-page.tsx new file mode 100644 index 0000000..aff25c0 --- /dev/null +++ b/src/pages/settings-page.tsx @@ -0,0 +1,37 @@ +import { useLogin } from "hooks/login"; +import { useNavigate } from "react-router-dom"; +import "./settings-page.css"; +import React from "react"; +import { Button } from "@getalby/bitcoin-connect-react"; +import Copy from "element/copy"; +import { hexToBech32 } from "@snort/shared"; + +export function SettingsPage() { + const navigate = useNavigate(); + const login = useLogin(); + + React.useEffect(() => { + if (!login) { + + navigate("/"); + } + }, [login]) + + + return ( +
+

Account

+ {login?.pubkey &&
+

Logged in as

+ +
} + {login?.privateKey &&
+

Private key

+ +
} + +

Zaps

+
+ ); +} diff --git a/webpack.config.js b/webpack.config.js index 7274007..bce7e04 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -75,6 +75,15 @@ const config = { exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, loader: require.resolve("source-map-loader"), + options: { + filterSourceMappingUrl: (url, resourcePath) => { + // disable warning for missing @scure-bip39 sourcemaps + if (/.*\/.yarn\/cache\/@scure-bip39.*/.test(resourcePath)) { + return false + } + return true + } + } }, { test: /\.tsx?$/i, diff --git a/yarn.lock b/yarn.lock index 3c6a695..fb34872 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1601,6 +1601,38 @@ __metadata: languageName: node linkType: hard +"@getalby/bitcoin-connect-react@npm:1.0.0": + version: 1.0.0 + resolution: "@getalby/bitcoin-connect-react@npm:1.0.0" + dependencies: + "@getalby/lightning-wallet-connect": ^1.0.14 + peerDependencies: + react: ^18.2.0 + checksum: a6cd01a07579b5bba0d92a596e3283f996f9446da50fa62852e5ced0418f219a65140f643eaf3f6853166587693fb347cd06485525f85e7cf53dfa254db9403a + languageName: node + linkType: hard + +"@getalby/lightning-wallet-connect@npm:^1.0.14": + version: 1.0.14 + resolution: "@getalby/lightning-wallet-connect@npm:1.0.14" + dependencies: + "@getalby/sdk": ^2.2.3 + lit: ^2.2.4 + zustand: ^4.4.1 + checksum: 4245bf5869492fcca1b22a5de312f2560e3b68a0b74b14bdd2dd403f6344c7c307035e8bfbf3132a204a122fd0169ab777dc3a66ab5200c4fd450183b72b9b8a + languageName: node + linkType: hard + +"@getalby/sdk@npm:^2.2.3": + version: 2.2.3 + resolution: "@getalby/sdk@npm:2.2.3" + dependencies: + crypto-js: ^4.1.1 + nostr-tools: 1.13.1 + checksum: 8753aaa9f6488db581278aadd9c8f705bc740dd7d87051d5441a9ac19a46d51d7987b7fd4712a52734a5577a823fcb42b7bf8240bef7ad10d0bc0a0116faa524 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.10": version: 0.11.10 resolution: "@humanwhocodes/config-array@npm:0.11.10" @@ -1738,6 +1770,31 @@ __metadata: languageName: node linkType: hard +"@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": + version: 1.1.1 + resolution: "@lit-labs/ssr-dom-shim@npm:1.1.1" + checksum: 7a7add78e3ee570a7b987b9bf85e700b20d35d31c8b54cf4c8b2e3c8458ed4e2b0ff328706e5be7887f0ca8a02878c186e76609defb78f0d1b3c0e6b47c9f6ef + languageName: node + linkType: hard + +"@lit/reactive-element@npm:^1.3.0, @lit/reactive-element@npm:^1.6.0": + version: 1.6.3 + resolution: "@lit/reactive-element@npm:1.6.3" + dependencies: + "@lit-labs/ssr-dom-shim": ^1.0.0 + checksum: 79b58631c38effeabad090070324431da8a22cf0ff665f5e4de35e4d791f984742b3d340c9c7fce996d1124a8da95febc582471b4c237236c770b1300b56ef6e + languageName: node + linkType: hard + +"@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" + dependencies: + "@noble/hashes": 1.3.1 + checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 + languageName: node + linkType: hard + "@noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -1747,7 +1804,14 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2": +"@noble/hashes@npm:1.3.1": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 @@ -2379,13 +2443,34 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.2": +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.2, @scure/base@npm:~1.1.0": version: 1.1.2 resolution: "@scure/base@npm:1.1.2" checksum: f666b09dbd62ecb5fe6d0e7a629c8a86a972a47dc4f4555ebbbd7b09782b10a5f894fed9c3b8c74fd683b1588c064df079a44e9f695c075ccd98c30a8d3e91f7 languageName: node linkType: hard +"@scure/bip32@npm:1.3.1": + version: 1.3.1 + resolution: "@scure/bip32@npm:1.3.1" + dependencies: + "@noble/curves": ~1.1.0 + "@noble/hashes": ~1.3.1 + "@scure/base": ~1.1.0 + checksum: 394d65f77a40651eba21a5096da0f4233c3b50d422864751d373fcf142eeedb94a1149f9ab1dbb078086dab2d0bc27e2b1afec8321bf22d4403c7df2fea5bfe2 + languageName: node + linkType: hard + +"@scure/bip39@npm:1.2.1": + version: 1.2.1 + resolution: "@scure/bip39@npm:1.2.1" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -4316,6 +4401,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:^4.1.1": + version: 4.1.1 + resolution: "crypto-js@npm:4.1.1" + checksum: b3747c12ee3a7632fab3b3e171ea50f78b182545f0714f6d3e7e2858385f0f4101a15f2517e033802ce9d12ba50a391575ff4638c9de3dd9b2c4bc47768d5425 + languageName: node + linkType: hard + "crypto-random-string@npm:^2.0.0": version: 2.0.0 resolution: "crypto-random-string@npm:2.0.0" @@ -6935,6 +7027,37 @@ __metadata: languageName: node linkType: hard +"lit-element@npm:^3.3.0": + version: 3.3.3 + resolution: "lit-element@npm:3.3.3" + dependencies: + "@lit-labs/ssr-dom-shim": ^1.1.0 + "@lit/reactive-element": ^1.3.0 + lit-html: ^2.8.0 + checksum: 29a596fa556e231cce7246ca3e5687ad238f299b0cb374a0934d5e6fe9adf1436e031d4fbd21b280aabfc0e21a66e6c4b52da558a908df2566d09d960f3ca93d + languageName: node + linkType: hard + +"lit-html@npm:^2.8.0": + version: 2.8.0 + resolution: "lit-html@npm:2.8.0" + dependencies: + "@types/trusted-types": ^2.0.2 + checksum: 2d70df07248bcb2f502a3afb1e91d260735024fa669669ffb1417575aa39c3092779725ac1b90f5f39e4ce78c63f431f51176bc67f532389f0285a6991573255 + languageName: node + linkType: hard + +"lit@npm:^2.2.4": + version: 2.8.0 + resolution: "lit@npm:2.8.0" + dependencies: + "@lit/reactive-element": ^1.6.0 + lit-element: ^3.3.0 + lit-html: ^2.8.0 + checksum: 2480e733f7d022d3ecba91abc58a20968f0ca8f5fa30b3341ecf4bcf4845e674ad27b721a5ae53529cafc6ca603c015b80d0979ceb7a711e268ef20bb6bc7527 + languageName: node + linkType: hard + "loader-runner@npm:^4.2.0": version: 4.3.0 resolution: "loader-runner@npm:4.3.0" @@ -7795,6 +7918,19 @@ __metadata: languageName: node linkType: hard +"nostr-tools@npm:1.13.1": + version: 1.13.1 + resolution: "nostr-tools@npm:1.13.1" + dependencies: + "@noble/curves": 1.1.0 + "@noble/hashes": 1.3.1 + "@scure/base": 1.1.1 + "@scure/bip32": 1.3.1 + "@scure/bip39": 1.2.1 + checksum: 49695f94b5d399a29c9d7173fa2927c24247e365a7b17586434c4b442b99b6a35b6b015c01736b12fcaaba5b88884e25c1af1367880111403ea0718db85d5467 + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -9781,6 +9917,7 @@ __metadata: "@emoji-mart/react": ^1.1.1 "@formatjs/cli": ^6.1.3 "@formatjs/ts-transformer": ^3.13.3 + "@getalby/bitcoin-connect-react": 1.0.0 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 "@radix-ui/react-collapsible": ^1.0.3 @@ -10579,6 +10716,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:1.2.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a + languageName: node + linkType: hard + "usehooks-ts@npm:^2.9.1": version: 2.9.1 resolution: "usehooks-ts@npm:2.9.1" @@ -11276,3 +11422,23 @@ __metadata: checksum: 2cac84540f65c64ccc1683c267edce396b26b1e931aa429660aefac8fbe0188167b7aee815a3c22fa59a28a58d898d1a2b1825048f834d8d629f4c2a5d443801 languageName: node linkType: hard + +"zustand@npm:^4.4.1": + version: 4.4.1 + resolution: "zustand@npm:4.4.1" + dependencies: + use-sync-external-store: 1.2.0 + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 80acd0fbf633782996642802c8692bbb80ae5c80a8dff4c501b88250acd5ccd468fbc6398bdce198475a25e3839c91385b81da921274f33ffb5c2d08c3eab400 + languageName: node + linkType: hard