mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 11:43:30 +00:00
polish nwc connection flow
This commit is contained in:
parent
fb8a6581dd
commit
432b2ae185
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@ out
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
*.db
|
*.db
|
||||||
*.db-journal
|
*.db-journal
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
pnpm exec lint-staged
|
bun run lint-staged
|
||||||
|
22
README.md
22
README.md
@ -8,6 +8,12 @@ Download Lume for your platform here: [https://github.com/luminous-devs/lume/rel
|
|||||||
|
|
||||||
Supported platform: macOS, Windows and Linux
|
Supported platform: macOS, Windows and Linux
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Bun: https://bun.sh/docs/installation
|
||||||
|
|
||||||
|
- Tauri: https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-macos
|
||||||
|
|
||||||
### Develop
|
### Develop
|
||||||
|
|
||||||
Clone project
|
Clone project
|
||||||
@ -19,23 +25,17 @@ git clone https://github.com/luminous-devs/lume.git && cd lume
|
|||||||
Install packages
|
Install packages
|
||||||
|
|
||||||
```
|
```
|
||||||
pnpm install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
Run dev
|
Run dev build
|
||||||
|
|
||||||
```
|
```
|
||||||
pnpm tauri dev
|
bun tauri dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Build
|
Generate production build
|
||||||
|
|
||||||
```
|
```
|
||||||
pnpm tauri build
|
bun tauri build
|
||||||
```
|
|
||||||
|
|
||||||
(Advance) - Generate SQLite migration
|
|
||||||
|
|
||||||
```
|
|
||||||
pnpm add-migrate <migrate_name>
|
|
||||||
```
|
```
|
@ -11,7 +11,7 @@
|
|||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"lint": "eslint ./src --fix",
|
"lint": "eslint ./src --fix",
|
||||||
"format": "prettier ./src --write",
|
"format": "prettier ./src --write",
|
||||||
"dep-update": "pnpm update && cd src-tauri/ && cargo update"
|
"dep-update": "bun update && cd src-tauri/ && cargo update"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"**/*.{ts, tsx}": "eslint --fix",
|
"**/*.{ts, tsx}": "eslint --fix",
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeBuildCommand": "pnpm build",
|
"beforeBuildCommand": "bun run build",
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "bun run dev",
|
||||||
"devPath": "http://localhost:3000",
|
"devPath": "http://localhost:3000",
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
"withGlobalTauri": true
|
"withGlobalTauri": true
|
||||||
|
@ -56,7 +56,7 @@ export function UnlockScreen() {
|
|||||||
if (!db.secureDB) db.secureDB = stronghold;
|
if (!db.secureDB) db.secureDB = stronghold;
|
||||||
|
|
||||||
const privkey = await db.secureLoad(db.account.pubkey);
|
const privkey = await db.secureLoad(db.account.pubkey);
|
||||||
const uri = await db.secureLoad('walletConnectURL', 'alby');
|
const uri = await db.secureLoad('walletConnectURL', 'nwc');
|
||||||
|
|
||||||
if (privkey) setPrivkey(privkey);
|
if (privkey) setPrivkey(privkey);
|
||||||
if (uri) setWalletConnectURL(uri);
|
if (uri) setWalletConnectURL(uri);
|
||||||
|
@ -45,7 +45,7 @@ export function NWCAlby() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
webview.listen('tauri://close-requested', async () => {
|
webview.listen('tauri://close-requested', async () => {
|
||||||
await db.secureSave('walletConnectURL', walletConnectURL, 'alby');
|
await db.secureSave('walletConnectURL', walletConnectURL, 'nwc');
|
||||||
setWalletConnectURL(walletConnectURL);
|
setWalletConnectURL(walletConnectURL);
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
setIsloading(false);
|
setIsloading(false);
|
||||||
@ -59,7 +59,7 @@ export function NWCAlby() {
|
|||||||
return (
|
return (
|
||||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2.5">
|
||||||
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-orange-200">
|
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-orange-200">
|
||||||
<AlbyIcon className="h-8 w-8" />
|
<AlbyIcon className="h-8 w-8" />
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +71,7 @@ export function NWCAlby() {
|
|||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex h-8 w-min items-center justify-center rounded-md bg-white/10 px-2.5 text-sm font-medium text-white hover:bg-white/20"
|
className="inline-flex h-8 w-min items-center justify-center rounded-md bg-white/10 px-2.5 text-sm font-medium text-white hover:bg-green-500"
|
||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</button>
|
</button>
|
||||||
|
@ -57,9 +57,10 @@ export function NWCOther() {
|
|||||||
const params = new URLSearchParams(uriObj.search);
|
const params = new URLSearchParams(uriObj.search);
|
||||||
|
|
||||||
if (params.has('relay') && params.has('secret')) {
|
if (params.has('relay') && params.has('secret')) {
|
||||||
await db.secureSave('walletConnectURL', data.uri, 'alby');
|
await db.secureSave('walletConnectURL', data.uri, 'nwc');
|
||||||
setWalletConnectURL(data.uri);
|
setWalletConnectURL(data.uri);
|
||||||
setIsloading(false);
|
setIsloading(false);
|
||||||
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsloading(false);
|
setIsloading(false);
|
||||||
@ -73,22 +74,22 @@ export function NWCOther() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<div className="flex items-center justify-between pt-3">
|
<div className="flex items-center justify-between pt-4">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2.5">
|
||||||
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-white/10">
|
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-white/10">
|
||||||
<WorldIcon className="h-5 w-5" />
|
<WorldIcon className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-semibold leading-tight text-white">URI String</h5>
|
<h5 className="font-semibold leading-tight text-white">URI String</h5>
|
||||||
<p className="text-sm leading-tight text-white/50">
|
<p className="text-sm leading-tight text-white/50">
|
||||||
Using format nostr+walletconnect://
|
Using format nostr+walletconnect:
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex h-8 w-min items-center justify-center rounded-md bg-white/10 px-2.5 text-sm font-medium text-white hover:bg-white/20"
|
className="inline-flex h-8 w-min items-center justify-center rounded-md bg-white/10 px-2.5 text-sm font-medium text-white hover:bg-green-500"
|
||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</button>
|
</button>
|
||||||
@ -123,6 +124,7 @@ export function NWCOther() {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register('uri', { required: true })}
|
{...register('uri', { required: true })}
|
||||||
|
placeholder="nostr+walletconnect:"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
|
@ -1,20 +1,96 @@
|
|||||||
import { NWCAlby } from '@app/nwc/components/alby';
|
import { NWCAlby } from '@app/nwc/components/alby';
|
||||||
import { NWCOther } from '@app/nwc/components/other';
|
import { NWCOther } from '@app/nwc/components/other';
|
||||||
|
|
||||||
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
|
import { CheckCircleIcon } from '@shared/icons';
|
||||||
|
|
||||||
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
export function NWCScreen() {
|
export function NWCScreen() {
|
||||||
|
const { db } = useStorage();
|
||||||
|
|
||||||
|
const [walletConnectURL, setWalletConnectURL] = useStronghold((state) => [
|
||||||
|
state.walletConnectURL,
|
||||||
|
state.setWalletConnectURL,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const remove = async () => {
|
||||||
|
setWalletConnectURL('');
|
||||||
|
await db.secureSave('walletConnectURL', '', 'nwc');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<div className="flex w-full flex-col gap-5">
|
<div className="flex w-full flex-col gap-5">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h3 className="text-2xl font-bold leading-tight">Nostr Wallet Connect</h3>
|
<h3 className="text-2xl font-bold leading-tight">
|
||||||
|
Nostr Wallet Connect (Beta)
|
||||||
|
</h3>
|
||||||
<p className="leading-tight text-white/50">
|
<p className="leading-tight text-white/50">
|
||||||
Sending tips easily via Bitcoin Lightning.
|
Sending tips easily via Bitcoin Lightning.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto flex w-full max-w-lg flex-col gap-3 divide-y divide-white/5 rounded-xl bg-white/10 p-3">
|
<div className="mx-auto max-w-lg">
|
||||||
|
{!walletConnectURL ? (
|
||||||
|
<div className="flex w-full flex-col gap-4 divide-y divide-white/5 rounded-xl bg-white/10 p-3">
|
||||||
<NWCAlby />
|
<NWCAlby />
|
||||||
<NWCOther />
|
<NWCOther />
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex w-full flex-col rounded-xl bg-white/10 p-3">
|
||||||
|
<div className="mb-1 inline-flex items-center gap-1.5 text-sm text-green-500">
|
||||||
|
<CheckCircleIcon className="h-4 w-4" />
|
||||||
|
<p>You're using nostr wallet connect</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
value={walletConnectURL}
|
||||||
|
className="relative h-40 w-full resize-none rounded-lg bg-white/10 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => remove()}
|
||||||
|
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-white/5 px-6 font-medium leading-none text-red-500 hover:bg-white/10 focus:outline-none disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Remove connection
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-5 flex flex-col gap-4">
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<h5 className="text-sm font-bold text-white/80">Introduction</h5>
|
||||||
|
<p className="text-sm text-white/50">
|
||||||
|
Nostr Wallet Connect (NWC) is a way for applications like Nostr clients to
|
||||||
|
access a remote Lightning wallet through a standardized protocol.
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-white/50">
|
||||||
|
To learn more about the details have a look at{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/getAlby/nips/blob/7-wallet-connect-patch/47.md"
|
||||||
|
target="_blank"
|
||||||
|
className="text-fuchsia-200"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
the specs (NIP47)
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<h5 className="text-sm font-bold text-white/80">About tipping</h5>
|
||||||
|
<p className="text-sm text-white/50">
|
||||||
|
Also known as Zap in other Nostr client.
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-white/50">
|
||||||
|
Lume doesn't take any commission or platform fees when you tip
|
||||||
|
someone.
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-white/50">Lume doesn't hold your Bitcoin</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,11 +3,11 @@ import { SendPaymentResponse } from '@getalby/sdk/dist/types';
|
|||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import { message } from '@tauri-apps/api/dialog';
|
import { message } from '@tauri-apps/api/dialog';
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import CurrencyInput from 'react-currency-input-field';
|
import CurrencyInput from 'react-currency-input-field';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
|
|
||||||
import { AlbyIcon, CancelIcon, ZapIcon } from '@shared/icons';
|
import { CancelIcon, ZapIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { useStronghold } from '@stores/stronghold';
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
const [isCompleted, setIsCompleted] = useState(false);
|
const [isCompleted, setIsCompleted] = useState(false);
|
||||||
|
|
||||||
const walletConnectURL = useStronghold((state) => state.walletConnectURL);
|
const walletConnectURL = useStronghold((state) => state.walletConnectURL);
|
||||||
|
const nwc = useRef(null);
|
||||||
|
|
||||||
const createZapRequest = async () => {
|
const createZapRequest = async () => {
|
||||||
try {
|
try {
|
||||||
@ -41,16 +42,16 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
|
|
||||||
// user don't connect Alby, create QR Code for invoice
|
// user don't connect nwc, create QR Code for invoice
|
||||||
if (!walletConnectURL) return setInvoice(res);
|
if (!walletConnectURL) return setInvoice(res);
|
||||||
|
|
||||||
// user connect Alby
|
// user connect nwc
|
||||||
const nwc = new webln.NostrWebLNProvider({
|
nwc.current = new webln.NostrWebLNProvider({
|
||||||
nostrWalletConnectUrl: walletConnectURL,
|
nostrWalletConnectUrl: walletConnectURL,
|
||||||
});
|
});
|
||||||
await nwc.enable();
|
await nwc.current.enable();
|
||||||
|
|
||||||
const send: SendPaymentResponse = await nwc.sendPayment(res);
|
const send: SendPaymentResponse = await nwc.current.sendPayment(res);
|
||||||
if (send) {
|
if (send) {
|
||||||
await sendNativeNotification(
|
await sendNativeNotification(
|
||||||
`You've tipped ${compactNumber.format(send.amount)} sats to ${
|
`You've tipped ${compactNumber.format(send.amount)} sats to ${
|
||||||
@ -59,7 +60,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// eose
|
// eose
|
||||||
nwc.close();
|
nwc.current.close();
|
||||||
setIsCompleted(true);
|
setIsCompleted(true);
|
||||||
|
|
||||||
// reset after 3 secs
|
// reset after 3 secs
|
||||||
@ -67,6 +68,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
nwc.current.close();
|
||||||
await message(JSON.stringify(e), { title: 'Zap', type: 'error' });
|
await message(JSON.stringify(e), { title: 'Zap', type: 'error' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -177,10 +179,18 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => createZapRequest()}
|
onClick={() => createZapRequest()}
|
||||||
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-orange-100 px-4 font-medium text-black hover:bg-orange-200"
|
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-fuchsia-500 px-4 font-medium text-white hover:bg-fuchsia-600"
|
||||||
>
|
>
|
||||||
<p>{isCompleted ? 'Successfully tipped' : 'Tip with Alby'}</p>
|
{isCompleted ? (
|
||||||
<AlbyIcon className="h-6 w-6" />
|
<p>Successfully tipped</p>
|
||||||
|
) : (
|
||||||
|
<span className="flex flex-col">
|
||||||
|
<p className="mb-px leading-none">Send tip</p>
|
||||||
|
<p className="text-xs leading-none text-white/70">
|
||||||
|
You're using nostr wallet connect
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
@ -191,10 +201,6 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
<p>Create Lightning invoice</p>
|
<p>Create Lightning invoice</p>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<span className="text-center text-sm leading-tight text-white/50">
|
|
||||||
The recipient receives 100% of the amount that you send. Lume does
|
|
||||||
not take any commission, and you cannot get refund
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
Loading…
Reference in New Issue
Block a user