diff --git a/src/element/login-signup.tsx b/src/element/login-signup.tsx index 5d49fd8..94b2c43 100644 --- a/src/element/login-signup.tsx +++ b/src/element/login-signup.tsx @@ -1,43 +1,50 @@ import "./login-signup.css"; +import LoginHeader from "../login-start.png"; +import LoginVault from "../login-vault.png"; +import LoginProfile from "../login-profile.png"; +import LoginKey from "../login-key.png"; +import LoginWallet from "../login-wallet.png"; + import { CSSProperties, useState } from "react"; +import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; import { EventPublisher, UserMetadata } from "@snort/system"; import { schnorr } from "@noble/curves/secp256k1"; import { bytesToHex } from "@noble/curves/abstract/utils"; +import { LNURL, bech32ToHex, getPublicKey } from "@snort/shared"; +import { VoidApi } from "@void-cat/api"; import AsyncButton from "./async-button"; import { Login, System } from "index"; import { Icon } from "./icon"; import Copy from "./copy"; import { hexToBech32, openFile } from "utils"; -import { VoidApi } from "@void-cat/api"; -import { FormattedMessage } from "react-intl"; -import { bech32 } from "@scure/base"; +import { LoginType } from "login"; +import { DefaultProvider, StreamProviderInfo } from "providers"; +import { Nip103StreamProvider } from "providers/zsz"; enum Stage { Login = 0, - Details = 1, - SaveKey = 2, + LoginInput = 1, + Details = 2, + LnAddress = 3, + SaveKey = 4, } export function LoginSignup({ close }: { close: () => void }) { const [error, setError] = useState(""); const [stage, setStage] = useState(Stage.Login); const [username, setUsername] = useState(""); + const [lnAddress, setLnAddress] = useState(""); + const [providerInfo, setProviderInfo] = useState(); const [avatar, setAvatar] = useState(""); const [key, setNewKey] = useState(""); + const { formatMessage } = useIntl(); + const hasNostrExtension = "nostr" in window && window.nostr; function doLoginNsec() { try { - 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); + const hexKey = key.startsWith("nsec") ? bech32ToHex(key) : key; + Login.loginWithPrivateKey(hexKey); close(); } catch (e) { console.error(e); @@ -49,9 +56,25 @@ export function LoginSignup({ close }: { close: () => void }) { } } + async function loginNip7() { + try { + const nip7 = await EventPublisher.nip7(); + if (nip7) { + Login.loginWithPubkey(nip7.pubKey, LoginType.Nip7); + } + } catch (e) { + if (e instanceof Error) { + setError(e.message); + } else { + setError(e as string); + } + } + } + function createAccount() { const newKey = bytesToHex(schnorr.utils.randomPrivateKey()); setNewKey(newKey); + setLnAddress(`${getPublicKey(newKey)}@zap.stream`) setStage(Stage.Details); } @@ -78,93 +101,202 @@ export function LoginSignup({ close }: { close: () => void }) { } } + async function setupProfile() { + const px = new Nip103StreamProvider(DefaultProvider.name, DefaultProvider.url, EventPublisher.privateKey(key)); + const info = await px.info(); + setProviderInfo(info); + + setStage(Stage.LnAddress) + } + async function saveProfile() { - const pub = EventPublisher.privateKey(key); - const profile = { - name: username, - picture: avatar, - lud16: `${pub.pubKey}@zap.stream`, - } as UserMetadata; + try { + // validate LN addreess + try { + const lnurl = new LNURL(lnAddress); + await lnurl.load(); + } catch { + throw new Error(formatMessage({ + defaultMessage: "Hmm, your lightning address looks wrong" + })); + } + const pub = EventPublisher.privateKey(key); + const profile = { + name: username, + picture: avatar, + lud16: lnAddress, + } as UserMetadata; - const ev = await pub.metadata(profile); - console.debug(ev); - System.BroadcastEvent(ev); + const ev = await pub.metadata(profile); + console.debug(ev); + System.BroadcastEvent(ev); - setStage(Stage.SaveKey); + setStage(Stage.SaveKey); + } catch (e) { + if (e instanceof Error) { + setError(e.message); + } else { + setError(e as string); + } + } } switch (stage) { case Stage.Login: { return ( <> -

- -

-

- -

- -
-
- -
+ +
+

+ +

+

+ +

+ + +
+
+ +
+
+ {hasNostrExtension && <> + + + + } + + {error && {error}}
- - {error && {error}} ); } + case Stage.LoginInput: { + return ( + <> + +
+

+ +

+

+ + + + }} /> +

+
+ setNewKey(e.target.value)} placeholder={formatMessage({ defaultMessage: "eg. nsec1xyz" })} /> +
+
+
+
+ + + + +
+
+ {error && {error}} +
+ + ) + } case Stage.Details: { return ( <> -

- -

-
-
- + +
+

+ +

+
+
+ +
-
-
-
- setUsername(e.target.value)} /> +
+
+ setUsername(e.target.value)} /> +
+ + +
- - - + + +
- - - ); } + case Stage.LnAddress: { + return ( + <> + +
+

+ +

+

+ +

+ {providerInfo?.balance &&

+ + }} /> +

} +
+
+ setLnAddress(e.target.value)} /> +
+ + + +
+ {error && {error}} + + + +
+ + ) + } case Stage.SaveKey: { return ( <> -

- -

-

- -

-
- + +
+

+ +

+

+ +

+
+ +
+
- ); } diff --git a/src/element/new-stream.tsx b/src/element/new-stream.tsx index f8dfc32..98a3d90 100644 --- a/src/element/new-stream.tsx +++ b/src/element/new-stream.tsx @@ -101,8 +101,10 @@ export function NewStreamDialog(props: NewStreamDialogProps & StreamEditorProps) -
- setOpen(false)} /> +
+
+ setOpen(false)} /> +
diff --git a/src/index.css b/src/index.css index 44cfd53..fd26446 100644 --- a/src/index.css +++ b/src/index.css @@ -61,6 +61,10 @@ a { justify-content: center; } +.f-space { + justify-content: space-between; +} + .pill { background: #171717; padding: 4px 8px; @@ -108,7 +112,6 @@ a { align-items: center; justify-content: center; gap: 8px; - height: 44px; } .btn-block { @@ -232,7 +235,6 @@ div.paper { .dialog-content { display: flex; flex-direction: column; - align-items: center; gap: 12px; z-index: 2; background-color: #171717; @@ -242,7 +244,7 @@ div.paper { left: 50%; transform: translate(-50%, -50%); width: 90vw; - max-width: 430px; + max-width: 450px; max-height: 85vh; overflow-y: auto; } @@ -254,7 +256,6 @@ div.paper { width: 100%; display: flex; flex-direction: column; - align-items: center; padding: 25px; box-sizing: border-box; gap: 16px; diff --git a/src/lang.json b/src/lang.json index b4c6475..c27bd89 100644 --- a/src/lang.json +++ b/src/lang.json @@ -2,12 +2,18 @@ "+0zv6g": { "defaultMessage": "Image" }, + "+AcVD+": { + "defaultMessage": "No emails, just awesomeness!" + }, "+vVZ/G": { "defaultMessage": "Connect" }, "/0TOL5": { "defaultMessage": "Amount" }, + "/EvlqN": { + "defaultMessage": "nostr signer extension" + }, "/GCoTA": { "defaultMessage": "Clear" }, @@ -20,6 +26,9 @@ "1EYCdR": { "defaultMessage": "Tags" }, + "1qsXCO": { + "defaultMessage": "eg. name@wallet.com" + }, "2/2yg+": { "defaultMessage": "Add" }, @@ -32,9 +41,15 @@ "3adEeb": { "defaultMessage": "{n} viewers" }, + "3df560": { + "defaultMessage": "Login with private key" + }, "47FYwb": { "defaultMessage": "Cancel" }, + "4l69eO": { + "defaultMessage": "Hmm, your lightning address looks wrong" + }, "4l6vz1": { "defaultMessage": "Copy" }, @@ -83,12 +98,21 @@ "ESyhzp": { "defaultMessage": "Your comment for {name}" }, + "FjDlus": { + "defaultMessage": "You can always replace it with your own address later." + }, + "Fodi9+": { + "defaultMessage": "Get paid by viewers" + }, "G/yZLu": { "defaultMessage": "Remove" }, "Gq6x9o": { "defaultMessage": "Cover Image" }, + "H/bNs9": { + "defaultMessage": "Save this and keep it safe! If you lose this key, you won't be able to access your account ever again. Yep, it's that serious!" + }, "H5+NAX": { "defaultMessage": "Balance" }, @@ -101,6 +125,9 @@ "IJDKz3": { "defaultMessage": "Zap amount in {currency}" }, + "INlWvJ": { + "defaultMessage": "OR" + }, "JEsxDw": { "defaultMessage": "Uploading..." }, @@ -119,9 +146,6 @@ "KkIL3s": { "defaultMessage": "No, I am under 18" }, - "Ld5LAE": { - "defaultMessage": "Nostr uses private keys, please save yours, if you lose this key you wont be able to login to your account anymore!" - }, "LknBsU": { "defaultMessage": "Stream Key" }, @@ -137,6 +161,9 @@ "OWgHbg": { "defaultMessage": "Edit card" }, + "Oxqtyf": { + "defaultMessage": "We hooked you up with a lightning wallet so you can get paid by viewers right away!" + }, "Q3au2v": { "defaultMessage": "About {estimate}" }, @@ -182,6 +209,9 @@ "X2PZ7D": { "defaultMessage": "Create Goal" }, + "Z8ZOEY": { + "defaultMessage": "This method is insecure. We recommend using a {nostrlink}" + }, "ZmqxZs": { "defaultMessage": "You can change this later" }, @@ -203,12 +233,18 @@ "ebmhes": { "defaultMessage": "Nostr Extension" }, + "f6biFA": { + "defaultMessage": "Oh, and you have {n} sats of free streaming on us! 💜" + }, "fBI91o": { "defaultMessage": "Zap" }, "fc2iho": { "defaultMessage": "Add File" }, + "feZ/kG": { + "defaultMessage": "Login with Private Key (insecure)" + }, "hGQqkW": { "defaultMessage": "Schedule" }, @@ -263,6 +299,9 @@ "pO/lPX": { "defaultMessage": "Scheduled for {date}" }, + "r2Jjms": { + "defaultMessage": "Log In" + }, "rWBFZA": { "defaultMessage": "Sexually explicit material ahead!" }, @@ -284,12 +323,18 @@ "tG1ST3": { "defaultMessage": "Incoming Zap" }, + "tM6fNW": { + "defaultMessage": "Amazing! Continue.." + }, "thsiMl": { "defaultMessage": "terms and conditions" }, "tzMNF3": { "defaultMessage": "Status" }, + "u6uD94": { + "defaultMessage": "Create an Account" + }, "uYw2LD": { "defaultMessage": "Stream" }, @@ -314,6 +359,9 @@ "x82IOl": { "defaultMessage": "Mute" }, + "yzKwBQ": { + "defaultMessage": "eg. nsec1xyz" + }, "zVDHAu": { "defaultMessage": "Zap Alert" } diff --git a/src/login-header.png b/src/login-header.png deleted file mode 100644 index 61220d1..0000000 Binary files a/src/login-header.png and /dev/null differ diff --git a/src/login-key.png b/src/login-key.png new file mode 100644 index 0000000..3236e6e Binary files /dev/null and b/src/login-key.png differ diff --git a/src/login-profile.png b/src/login-profile.png new file mode 100644 index 0000000..4397c70 Binary files /dev/null and b/src/login-profile.png differ diff --git a/src/login-start.png b/src/login-start.png new file mode 100644 index 0000000..4a114e5 Binary files /dev/null and b/src/login-start.png differ diff --git a/src/login-vault.png b/src/login-vault.png new file mode 100644 index 0000000..ae296c6 Binary files /dev/null and b/src/login-vault.png differ diff --git a/src/login-wallet.png b/src/login-wallet.png new file mode 100644 index 0000000..52890de Binary files /dev/null and b/src/login-wallet.png differ diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index ceeb871..ceaa1af 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -13,9 +13,6 @@ 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(); @@ -65,16 +62,7 @@ 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); - } + function handleLogin() { setShowLogin(true); } @@ -87,10 +75,7 @@ export function LayoutPage() { - -
- setShowLogin(false)} /> -
+ setShowLogin(false)} />
diff --git a/src/pages/providers/nostr.tsx b/src/pages/providers/nostr.tsx index 190fbaa..1890f2b 100644 --- a/src/pages/providers/nostr.tsx +++ b/src/pages/providers/nostr.tsx @@ -15,7 +15,7 @@ export function ConfigureNostrType() { async function tryConnect() { try { - const api = new Nip103StreamProvider(url); + const api = new Nip103StreamProvider(new URL(url).host, url); const inf = await api.info(); setInfo(inf); } catch (e) { @@ -58,7 +58,7 @@ export function ConfigureNostrType() {