From f82bd5f681165483c62efd009f4b69797981360d Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Thu, 4 Jul 2024 15:08:21 +0530 Subject: [PATCH 01/47] chore: push card footer to bottom (#562) --- frontend/src/screens/channels/Channels.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 2105308d..9e356730 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -414,14 +414,14 @@ export default function Channels() { )} > {showHostedBalance && ( - + Alby Hosted Balance - +
{new Intl.NumberFormat().format(albyBalance?.sats)} sats
@@ -476,14 +476,14 @@ export default function Channels() {
)} - + Savings Balance - + {!balances && (
@@ -519,14 +519,14 @@ export default function Channels() { - + Spending Balance - + {!balances && (
@@ -549,14 +549,14 @@ export default function Channels() { - + Receiving Capacity - +
{balances && new Intl.NumberFormat().format( From 6eb9d946b1e0057b30cc4fb1aed8956582ad9cf8 Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:03:58 +0700 Subject: [PATCH 02/47] fix: drain alby shared funds calculation (#569) --- alby/alby_oauth_service.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/alby/alby_oauth_service.go b/alby/alby_oauth_service.go index dcfd9b89..704ea52e 100644 --- a/alby/alby_oauth_service.go +++ b/alby/alby_oauth_service.go @@ -258,15 +258,18 @@ func (svc *albyOAuthService) DrainSharedWallet(ctx context.Context, lnClient lnc return err } - amount := int64(math.Floor( - float64(balance.Balance)*1000* // Alby shared node balance in sats - (1-8/1000)* // Alby service fee (0.8%) - 0.99)) - // Maximum potential routing fees (1%) - 10000 // Alby fee reserve (10 sats) + balanceSat := float64(balance.Balance) - if amount < 1000 { + amountSat := int64(math.Floor( + balanceSat- // Alby shared node balance in sats + (balanceSat*(8/1000))- // Alby service fee (0.8%) + (balanceSat*0.01))) - // Maximum potential routing fees (1%) + 10 // Alby fee reserve (10 sats) + + if amountSat < 1 { return errors.New("Not enough balance remaining") } + amount := amountSat * 1000 logger.Logger.WithField("amount", amount).WithError(err).Error("Draining Alby shared wallet funds") From 1d46d8da55b555dc4da26e2910266e274a63c1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Thu, 4 Jul 2024 16:23:26 +0200 Subject: [PATCH 03/47] fix: remove shadows from cards --- frontend/src/components/ui/card.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx index ed176639..0f76508b 100644 --- a/frontend/src/components/ui/card.tsx +++ b/frontend/src/components/ui/card.tsx @@ -8,10 +8,7 @@ const Card = React.forwardRef< >(({ className, ...props }, ref) => (
)); @@ -75,9 +72,9 @@ CardFooter.displayName = "CardFooter"; export { Card, - CardHeader, + CardContent, + CardDescription, CardFooter, + CardHeader, CardTitle, - CardDescription, - CardContent, }; From 46f45046556f8b4c8a6ce5789d705b2e4fc7810a Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Thu, 4 Jul 2024 20:00:11 +0530 Subject: [PATCH 04/47] feat: update start api to be async (#558) Co-authored-by: Roland Bewick --- .../components/redirects/SetupRedirect.tsx | 10 +--- frontend/src/screens/Start.tsx | 56 ++++++++++++++++--- frontend/src/screens/setup/SetupFinish.tsx | 35 ++++++++---- http/http_service.go | 16 ++++-- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/frontend/src/components/redirects/SetupRedirect.tsx b/frontend/src/components/redirects/SetupRedirect.tsx index 9bf61152..207ed1bf 100644 --- a/frontend/src/components/redirects/SetupRedirect.tsx +++ b/frontend/src/components/redirects/SetupRedirect.tsx @@ -3,7 +3,6 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom"; import Loading from "src/components/Loading"; import { useInfo } from "src/hooks/useInfo"; -let didSetupThisSession = false; export function SetupRedirect() { const { data: info } = useInfo(); const location = useLocation(); @@ -13,17 +12,10 @@ export function SetupRedirect() { if (!info) { return; } - if (didSetupThisSession) { - // ensure redirect does not happen as node may still be starting - // which would then incorrectly redirect to the login page - console.info("Skipping setup redirect on initial setup"); - return; - } - if (info.setupCompleted) { + if (info.setupCompleted && info.running) { navigate("/"); return; } - didSetupThisSession = true; }, [info, location, navigate]); if (!info) { diff --git a/frontend/src/screens/Start.tsx b/frontend/src/screens/Start.tsx index fcd2693f..58a52fe4 100644 --- a/frontend/src/screens/Start.tsx +++ b/frontend/src/screens/Start.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { useNavigate } from "react-router-dom"; import Container from "src/components/Container"; import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader"; import { Input } from "src/components/ui/input"; @@ -11,18 +10,61 @@ import { useInfo } from "src/hooks/useInfo"; import { handleRequestError } from "src/utils/handleRequestError"; import { request } from "src/utils/request"; +const messages: string[] = [ + "Unlocking", + "Starting the wallet", + "Connecting to the network", + "Syncing", + "Still syncing, please wait...", +]; + export default function Start() { const [unlockPassword, setUnlockPassword] = React.useState(""); const [loading, setLoading] = React.useState(false); - const navigate = useNavigate(); + const [buttonText, setButtonText] = React.useState("Login"); + useInfo(true); // poll the info endpoint to auto-redirect when app is running const { data: csrf } = useCSRF(); - const { mutate: refetchInfo } = useInfo(); const { toast } = useToast(); + React.useEffect(() => { + if (!loading) { + return; + } + let messageIndex = 1; + const intervalId = setInterval(() => { + if (messageIndex < messages.length) { + setButtonText(messages[messageIndex]); + messageIndex++; + } else { + clearInterval(intervalId); + } + }, 5000); + + const timeoutId = setTimeout(() => { + // if redirection didn't happen in 3 minutes info.running is false + toast({ + title: "Failed to start", + description: "Please try starting the node again.", + variant: "destructive", + }); + + setLoading(false); + setButtonText("Login"); + setUnlockPassword(""); + return; + }, 180000); // wait for 3 minutes + + return () => { + clearInterval(intervalId); + clearTimeout(timeoutId); + }; + }, [loading, toast]); + async function onSubmit(e: React.FormEvent) { e.preventDefault(); try { setLoading(true); + setButtonText(messages[0]); if (!csrf) { throw new Error("csrf not loaded"); } @@ -36,13 +78,11 @@ export default function Start() { unlockPassword, }), }); - await refetchInfo(); - - navigate("/"); } catch (error) { handleRequestError(toast, "Failed to connect", error); - } finally { setLoading(false); + setButtonText("Login"); + setUnlockPassword(""); } } @@ -68,7 +108,7 @@ export default function Start() { />
- Login + {buttonText}
diff --git a/frontend/src/screens/setup/SetupFinish.tsx b/frontend/src/screens/setup/SetupFinish.tsx index dfcfa81e..9ccda8b2 100644 --- a/frontend/src/screens/setup/SetupFinish.tsx +++ b/frontend/src/screens/setup/SetupFinish.tsx @@ -15,9 +15,9 @@ import { request } from "src/utils/request"; export function SetupFinish() { const navigate = useNavigate(); const { nodeInfo, unlockPassword } = useSetupStore(); - - const { mutate: refetchInfo } = useInfo(); + useInfo(true); // poll the info endpoint to auto-redirect when app is running const { data: csrf } = useCSRF(); + const [loading, setLoading] = React.useState(false); const [connectionError, setConnectionError] = React.useState(false); const hasFetchedRef = React.useRef(false); @@ -30,6 +30,22 @@ export function SetupFinish() { }, }; + useEffect(() => { + if (!loading) { + return; + } + const timer = setTimeout(() => { + // SetupRedirect takes care of redirection once info.running is true + // if it still didn't redirect after 3 minutes, we show an error + setLoading(false); + setConnectionError(true); + }, 180000); + + return () => { + clearTimeout(timer); + }; + }, [loading]); + useEffect(() => { // ensure setup call is only called once if (!csrf || hasFetchedRef.current) { @@ -38,18 +54,15 @@ export function SetupFinish() { hasFetchedRef.current = true; (async () => { + setLoading(true); const succeeded = await finishSetup(csrf, nodeInfo, unlockPassword); - if (succeeded) { - const info = await refetchInfo(); - if (!info) { - throw new Error("Failed to re-fetch info"); - } - navigate("/"); - } else { + // only setup call is successful as start is async + if (!succeeded) { + setLoading(false); setConnectionError(true); } })(); - }, [csrf, nodeInfo, refetchInfo, navigate, unlockPassword]); + }, [csrf, nodeInfo, navigate, unlockPassword]); if (connectionError) { return ( @@ -57,7 +70,7 @@ export function SetupFinish() {

Connection Failed

-

Please check your node configuration.

+

Please check your node configuration and try again.

)}
From 8d93c4a6cd7c27dc3eb1863c490d2a4631d6ba5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= <100827540+reneaaron@users.noreply.github.com> Date: Fri, 5 Jul 2024 10:09:29 +0200 Subject: [PATCH 16/47] fix: hide breadcrumb where not needed (#10) * fix: hide breadcrumb where not needed * Update frontend/src/components/Breadcrumbs.tsx * fix: code formatting --- frontend/src/components/AppHeader.tsx | 10 ++++++++-- frontend/src/components/Breadcrumbs.tsx | 4 ++-- frontend/src/components/layouts/SettingsLayout.tsx | 1 + frontend/src/screens/appstore/AppStore.tsx | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/AppHeader.tsx b/frontend/src/components/AppHeader.tsx index a4e59af4..8bcb3e53 100644 --- a/frontend/src/components/AppHeader.tsx +++ b/frontend/src/components/AppHeader.tsx @@ -5,12 +5,18 @@ type Props = { title: string | ReactElement; description: string | ReactElement; contentRight?: React.ReactNode; + breadcrumb?: boolean; }; -function AppHeader({ title, description, contentRight }: Props) { +function AppHeader({ + title, + description, + contentRight, + breadcrumb = true, +}: Props) { return ( <> - + {breadcrumb && }

{title}

diff --git a/frontend/src/components/Breadcrumbs.tsx b/frontend/src/components/Breadcrumbs.tsx index ed0085df..1ab01725 100644 --- a/frontend/src/components/Breadcrumbs.tsx +++ b/frontend/src/components/Breadcrumbs.tsx @@ -38,8 +38,8 @@ function Breadcrumbs() { // Remove the last item if it's an index route to prevent e.g. Wallet > Wallet const filteredCrumbs = isIndexRoute ? crumbs.slice(0, -1) : crumbs; - // Don't render anything if there is only one item - if (filteredCrumbs.length < 2) { + // Skip rendering for breadcrumbs consisting of 2 (or less) items + if (filteredCrumbs.length < 3) { return null; } diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index 312a8039..e3a6447b 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -62,6 +62,7 @@ export default function SettingsLayout() { diff --git a/frontend/src/screens/appstore/AppStore.tsx b/frontend/src/screens/appstore/AppStore.tsx index f25904c3..966bddc5 100644 --- a/frontend/src/screens/appstore/AppStore.tsx +++ b/frontend/src/screens/appstore/AppStore.tsx @@ -7,7 +7,7 @@ function AppStore() { return ( <> From 30883734778b29c19385f6f6bff669a789e17ea0 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 5 Jul 2024 20:32:40 +0700 Subject: [PATCH 17/47] chore: rename everything to alby hub --- .do/deploy.template.yaml | 10 ++++---- .github/workflows/http.yml | 2 +- .github/workflows/wails.yml | 8 +++---- Dockerfile | 2 +- README.md | 24 ++++++++++++------- alby/alby_oauth_service.go | 14 +++++------ alby/models.go | 4 ++-- api/api.go | 22 ++++++++--------- api/backup.go | 6 ++--- api/esplora.go | 2 +- api/lsp.go | 8 +++---- api/models.go | 6 ++--- cmd/http/main.go | 6 ++--- config/config.go | 4 ++-- db/db.go | 4 ++-- db/db_service.go | 4 ++-- docker-compose.yml | 2 +- events/events.go | 2 +- fly.toml | 2 +- frontend/src/screens/Welcome.tsx | 2 +- go.mod | 2 +- http/alby_http_service.go | 8 +++---- http/http_service.go | 16 ++++++------- lnclient/breez/breez.go | 4 ++-- lnclient/breez/breez_stub.go | 2 +- lnclient/cashu/cashu.go | 4 ++-- lnclient/greenlight/greenlight.go | 8 +++---- lnclient/greenlight/models.go | 2 +- lnclient/ldk/ldk.go | 14 +++++------ lnclient/ldk/ldk_event_broadcaster.go | 4 ++-- lnclient/lnd/lnd.go | 6 ++--- lnclient/phoenixd/phoenixd.go | 4 ++-- main_wails.go | 6 ++--- nip47/controllers/decode_request.go | 4 ++-- nip47/controllers/get_balance_controller.go | 6 ++--- .../get_balance_controller_test.go | 6 ++--- nip47/controllers/get_info_controller.go | 10 ++++---- nip47/controllers/get_info_controller_test.go | 8 +++---- .../list_transactions_controller.go | 6 ++--- .../list_transactions_controller_test.go | 6 ++--- .../controllers/lookup_invoice_controller.go | 6 ++--- .../lookup_invoice_controller_test.go | 6 ++--- nip47/controllers/make_invoice_controller.go | 6 ++--- .../make_invoice_controller_test.go | 6 ++--- nip47/controllers/models.go | 2 +- .../multi_pay_invoice_controller.go | 10 ++++---- .../multi_pay_invoice_controller_test.go | 6 ++--- .../multi_pay_keysend_controller.go | 8 +++---- .../multi_pay_keysend_controller_test.go | 6 ++--- nip47/controllers/pay_invoice_controller.go | 10 ++++---- .../pay_invoice_controller_test.go | 6 ++--- nip47/controllers/pay_keysend_controller.go | 10 ++++---- .../pay_keysend_controller_test.go | 6 ++--- nip47/controllers/sign_message_controller.go | 6 ++--- nip47/event_handler.go | 14 +++++------ nip47/event_handler_test.go | 4 ++-- nip47/models/models.go | 2 +- nip47/nip47_service.go | 12 +++++----- nip47/notifications/models.go | 2 +- .../notifications/nip47_notification_queue.go | 4 ++-- nip47/notifications/nip47_notifier.go | 16 ++++++------- nip47/notifications/nip47_notifier_test.go | 8 +++---- nip47/permissions/permissions.go | 12 +++++----- nip47/permissions/permissions_test.go | 6 ++--- nip47/publish_nip47_info.go | 4 ++-- render.yaml | 2 +- scripts/README.md | 4 ++-- scripts/linux-x86_64/phoenixd/README.md | 15 ++++++------ .../linux-x86_64/phoenixd/docker-compose.yml | 2 +- service/keys/keys.go | 4 ++-- service/models.go | 10 ++++---- service/service.go | 24 +++++++++---------- service/start.go | 22 ++++++++--------- service/stop.go | 4 ++-- tests/create_app.go | 2 +- tests/mock_ln_client.go | 2 +- tests/test_service.go | 12 +++++----- version/version.go | 4 ++-- wails.json | 4 ++-- wails/wails_app.go | 6 ++--- wails/wails_handlers.go | 8 +++---- 81 files changed, 284 insertions(+), 279 deletions(-) diff --git a/.do/deploy.template.yaml b/.do/deploy.template.yaml index 53f03299..ffb6f337 100644 --- a/.do/deploy.template.yaml +++ b/.do/deploy.template.yaml @@ -5,10 +5,10 @@ spec: image: registry_type: GHCR registry: getalby - repository: nostr-wallet-connect-next + repository: hub instance_size_slug: basic-xxs envs: - - key: LDK_ESPLORA_SERVER - value: "https://electrs.albylabs.com" - - key: LOG_EVENTS - value: "true" + - key: LDK_ESPLORA_SERVER + value: "https://electrs.albylabs.com" + - key: LOG_EVENTS + value: "true" diff --git a/.github/workflows/http.yml b/.github/workflows/http.yml index f7831940..947e2c6e 100644 --- a/.github/workflows/http.yml +++ b/.github/workflows/http.yml @@ -94,7 +94,7 @@ jobs: env: CGO_ENABLED: 1 TAG: ${{ github.ref_name }} - run: go build ${{ env.GOTAGS }} -o build/bin/${{ env.PACKAGE_NAME }}/bin/${{ env.EXEC_NAME }} -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" cmd/http/main.go + run: go build ${{ env.GOTAGS }} -o build/bin/${{ env.PACKAGE_NAME }}/bin/${{ env.EXEC_NAME }} -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" cmd/http/main.go - name: Copy shared libraries to the output directory run: | diff --git a/.github/workflows/wails.yml b/.github/workflows/wails.yml index 3b89a147..23d10827 100644 --- a/.github/workflows/wails.yml +++ b/.github/workflows/wails.yml @@ -54,7 +54,7 @@ jobs: - name: Setup GoLang uses: actions/setup-go@v5 with: - go-version-file: './go.mod' + go-version-file: "./go.mod" - name: Get dependencies run: go get -v -t -d ./... @@ -101,17 +101,17 @@ jobs: - name: Build App if: runner.os == 'macOS' - run: wails build --platform darwin/universal -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform darwin/universal -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Build App if: runner.os == 'Linux' - run: wails build --platform linux/amd64 -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform linux/amd64 -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Build Windows App if: runner.os == 'Windows' - run: wails build --platform windows/amd64 -webview2 embed -o ${{ env.EXEC_NAME }}.exe -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform windows/amd64 -webview2 embed -o ${{ env.EXEC_NAME }}.exe -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Import Code-Signing Certificates for macOS diff --git a/Dockerfile b/Dockerfile index f72de98a..386c383f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ COPY . . COPY --from=frontend /build/frontend/dist ./frontend/dist RUN GOARCH=$(echo "$TARGETPLATFORM" | cut -d'/' -f2) go build \ - -ldflags="-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=$TAG'" \ + -ldflags="-X 'github.com/getAlby/hub/version.Tag=$TAG'" \ -o main cmd/http/main.go COPY ./build/docker/copy_dylibs.sh . diff --git a/README.md b/README.md index 406f7cce..5554dfe0 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ For more information refer to: ### Versioning - $ go run -ldflags="-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=v0.6.0'" cmd/http/main.go + $ go run -ldflags="-X 'github.com/getAlby/hub/version.Tag=v0.6.0'" cmd/http/main.go ### Windows @@ -137,9 +137,9 @@ Breez SDK requires gcc to build the Breez bindings. Run `choco install mingw` an - `CLIENT_NOSTR_PUBKEY`: if set, this service will only listen to events authored by this public key. You can set this to your own nostr public key. - `RELAY`: default: "wss://relay.getalby.com/v1" - `COOKIE_SECRET`: a randomly generated secret string. (only needed in http mode) -- `DATABASE_URI`: a sqlite filename. Default: $XDG_DATA_HOME/nostr-wallet-connect/nwc.db +- `DATABASE_URI`: a sqlite filename. Default: $XDG_DATA_HOME/albyhub/nwc.db - `PORT`: the port on which the app should listen on (default: 8080) -- `WORK_DIR`: directory to store NWC data files. Default: $XDG_DATA_HOME/nostr-wallet-connect +- `WORK_DIR`: directory to store NWC data files. Default: $XDG_DATA_HOME/albyhub - `LOG_LEVEL`: log level for the application. Higher is more verbose. Default: 4 (info) ### LND Backend parameters @@ -181,9 +181,9 @@ Create an OAuth client at the [Alby Developer Portal](https://getalby.com/develo Follow the steps to integrate Mutinynet with your NWC Next setup: -1. Configure your environment with the [Mutinynet LDK parameters](https://github.com/getAlby/nostr-wallet-connect-next#mutinynet) +1. Configure your environment with the [Mutinynet LDK parameters](https://github.com/getAlby/hub#mutinynet) -2. Proceed as described in the [Development](https://github.com/getAlby/nostr-wallet-connect-next#Development) section to run the frontend and backend +2. Proceed as described in the [Development](https://github.com/getAlby/hub#Development) section to run the frontend and backend 3. During onboarding, after setting your password and authorizing via Alby OAuth, you'll be directed to `/onboarding/lightning/migrate-alby`. Click "Skip For Now" to access your wallet interface @@ -334,11 +334,11 @@ Run NWC on your own node! ### Digital Ocean -[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/getAlby/nostr-wallet-connect-next/tree/master) +[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/getAlby/hub/tree/master) ### Render -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/getAlby/nostr-wallet-connect-next) +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/getAlby/hub) ### Fly @@ -380,10 +380,16 @@ LDK logs: _Tested on Linux only_ -`docker run -v ~/.local/share/alby-nwc:/data -e WORK_DIR='/data' -p 8080:8080 ghcr.io/getalby/nostr-wallet-connect-next:latest` +`docker run -v ~/.local/share/albyhub:/data -e WORK_DIR='/data' -p 8080:8080 ghcr.io/getalby/hub:latest` #### From Source _Tested on Linux only_ -`docker run -v ~/.local/share/alby-nwc:/data -e WORK_DIR='/data' -p 8080:8080 $(docker build -q .)` +##### Docker Compose + +`docker compose up` + +##### Manually + +`docker run -v ~/.local/share/albyhub:/data -e WORK_DIR='/data' -p 8080:8080 $(docker build -q .)` diff --git a/alby/alby_oauth_service.go b/alby/alby_oauth_service.go index 704ea52e..b9534915 100644 --- a/alby/alby_oauth_service.go +++ b/alby/alby_oauth_service.go @@ -16,13 +16,13 @@ import ( "golang.org/x/oauth2" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - nip47 "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + nip47 "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/service/keys" ) type albyOAuthService struct { diff --git a/alby/models.go b/alby/models.go index 62073afc..0392a93c 100644 --- a/alby/models.go +++ b/alby/models.go @@ -3,8 +3,8 @@ package alby import ( "context" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" ) type AlbyOAuthService interface { diff --git a/api/api.go b/api/api.go index d96473d8..71eed4e1 100644 --- a/api/api.go +++ b/api/api.go @@ -15,17 +15,17 @@ import ( "github.com/sirupsen/logrus" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service" - "github.com/getAlby/nostr-wallet-connect/service/keys" - "github.com/getAlby/nostr-wallet-connect/utils" - "github.com/getAlby/nostr-wallet-connect/version" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + permissions "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service" + "github.com/getAlby/hub/service/keys" + "github.com/getAlby/hub/utils" + "github.com/getAlby/hub/version" ) type api struct { diff --git a/api/backup.go b/api/backup.go index f264a2cf..d38f9023 100644 --- a/api/backup.go +++ b/api/backup.go @@ -16,9 +16,9 @@ import ( "crypto/rand" "crypto/sha256" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/utils" "golang.org/x/crypto/pbkdf2" ) diff --git a/api/esplora.go b/api/esplora.go index 8dd0e31a..d7c4af8d 100644 --- a/api/esplora.go +++ b/api/esplora.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/api/lsp.go b/api/lsp.go index cf85d5ae..2d3f0e2e 100644 --- a/api/lsp.go +++ b/api/lsp.go @@ -13,10 +13,10 @@ import ( "strings" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/lsp" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/lsp" + "github.com/getAlby/hub/utils" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" ) diff --git a/api/models.go b/api/models.go index 1f82c075..44b3b4d7 100644 --- a/api/models.go +++ b/api/models.go @@ -5,9 +5,9 @@ import ( "io" "time" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" ) type API interface { diff --git a/cmd/http/main.go b/cmd/http/main.go index a953049e..929dec5d 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -10,9 +10,9 @@ import ( "time" echologrus "github.com/davrux/echo-logrus/v4" - "github.com/getAlby/nostr-wallet-connect/http" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/http" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/labstack/echo/v4" log "github.com/sirupsen/logrus" ) diff --git a/config/config.go b/config/config.go index 0fad5cd4..54f956fd 100644 --- a/config/config.go +++ b/config/config.go @@ -7,8 +7,8 @@ import ( "fmt" "os" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" "gorm.io/gorm" "gorm.io/gorm/clause" ) diff --git a/db/db.go b/db/db.go index d6cbbbb3..acc908ed 100644 --- a/db/db.go +++ b/db/db.go @@ -3,8 +3,8 @@ package db import ( "fmt" - "github.com/getAlby/nostr-wallet-connect/db/migrations" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/db/migrations" + "github.com/getAlby/hub/logger" "github.com/glebarez/sqlite" "gorm.io/gorm" ) diff --git a/db/db_service.go b/db/db_service.go index 8f6b159f..5779b603 100644 --- a/db/db_service.go +++ b/db/db_service.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/docker-compose.yml b/docker-compose.yml index ad8d54b2..8c5ec501 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: albyhub: platform: linux/amd64 container_name: albyhub - image: ghcr.io/getalby/nostr-wallet-connect-next:latest + image: ghcr.io/getalby/hub:latest volumes: - ./albyhub-data:/data ports: diff --git a/events/events.go b/events/events.go index ac7ff3c7..48d768a0 100644 --- a/events/events.go +++ b/events/events.go @@ -5,7 +5,7 @@ import ( "slices" "sync" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/fly.toml b/fly.toml index 48a7d18e..59d73ca3 100644 --- a/fly.toml +++ b/fly.toml @@ -7,7 +7,7 @@ app = 'nwc' primary_region = 'lax' [build] - image = 'ghcr.io/getalby/nostr-wallet-connect-next:latest' + image = 'ghcr.io/getalby/hub:latest' [env] DATABASE_URI = '/data/nwc.db' diff --git a/frontend/src/screens/Welcome.tsx b/frontend/src/screens/Welcome.tsx index 4446dd16..d319d02d 100644 --- a/frontend/src/screens/Welcome.tsx +++ b/frontend/src/screens/Welcome.tsx @@ -166,7 +166,7 @@ export function Welcome() { Subject to the following terms, Alby grants to you a perpetual, worldwide license to the Application pursuant to the Apache-2.0 license. - (https://github.com/getAlby/nostr-wallet-connect-next?tab=Apache-2.0-1-ov-file#readme). + (https://github.com/getAlby/hub?tab=Apache-2.0-1-ov-file#readme). This EULA pertains solely to the Application and does not limit your rights under, or grant you rights that supersede, the license terms of any particular component. diff --git a/go.mod b/go.mod index 6a92bd48..0c4688e7 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/getAlby/nostr-wallet-connect +module github.com/getAlby/hub go 1.22.2 diff --git a/http/alby_http_service.go b/http/alby_http_service.go index 83a03823..22b93408 100644 --- a/http/alby_http_service.go +++ b/http/alby_http_service.go @@ -4,10 +4,10 @@ import ( "fmt" "net/http" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/labstack/echo/v4" ) diff --git a/http/http_service.go b/http/http_service.go index 4542a982..11e99ea0 100644 --- a/http/http_service.go +++ b/http/http_service.go @@ -14,14 +14,14 @@ import ( "github.com/labstack/echo/v4/middleware" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" - - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/frontend" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" + + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/frontend" ) type HttpService struct { diff --git a/lnclient/breez/breez.go b/lnclient/breez/breez.go index cefdbf8e..82bd1dd8 100644 --- a/lnclient/breez/breez.go +++ b/lnclient/breez/breez.go @@ -16,8 +16,8 @@ import ( decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" ) type BreezService struct { diff --git a/lnclient/breez/breez_stub.go b/lnclient/breez/breez_stub.go index 9692e0ef..e7169c88 100644 --- a/lnclient/breez/breez_stub.go +++ b/lnclient/breez/breez_stub.go @@ -3,7 +3,7 @@ package breez import ( - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) func NewBreezService(mnemonic, apiKey, inviteCode, workDir string) (result lnclient.LNClient, err error) { diff --git a/lnclient/cashu/cashu.go b/lnclient/cashu/cashu.go index 4128d16c..d858a59f 100644 --- a/lnclient/cashu/cashu.go +++ b/lnclient/cashu/cashu.go @@ -11,8 +11,8 @@ import ( "github.com/elnosh/gonuts/wallet" "github.com/elnosh/gonuts/wallet/storage" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" ) diff --git a/lnclient/greenlight/greenlight.go b/lnclient/greenlight/greenlight.go index 7b2f1a12..300b7e10 100644 --- a/lnclient/greenlight/greenlight.go +++ b/lnclient/greenlight/greenlight.go @@ -13,15 +13,15 @@ import ( "strings" "time" - //"github.com/getAlby/nostr-wallet-connect/glalby" // for local development only + //"github.com/getAlby/hub/glalby" // for local development only "github.com/getAlby/glalby-go/glalby" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" ) type GreenlightService struct { diff --git a/lnclient/greenlight/models.go b/lnclient/greenlight/models.go index 4e9fddce..50e06097 100644 --- a/lnclient/greenlight/models.go +++ b/lnclient/greenlight/models.go @@ -1,6 +1,6 @@ package greenlight -import "github.com/getAlby/nostr-wallet-connect/lnclient" +import "github.com/getAlby/hub/lnclient" type NodeInfo struct { ID string `json:"id"` diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 82b949fc..e3a5e3b9 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -16,19 +16,19 @@ import ( "time" "github.com/getAlby/ldk-node-go/ldk_node" - // "github.com/getAlby/nostr-wallet-connect/ldk_node" + // "github.com/getAlby/hub/ldk_node" "encoding/hex" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/lsp" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/lsp" + "github.com/getAlby/hub/utils" ) type LDKService struct { diff --git a/lnclient/ldk/ldk_event_broadcaster.go b/lnclient/ldk/ldk_event_broadcaster.go index 4b734b08..2dd46f40 100644 --- a/lnclient/ldk/ldk_event_broadcaster.go +++ b/lnclient/ldk/ldk_event_broadcaster.go @@ -6,8 +6,8 @@ import ( "time" "github.com/getAlby/ldk-node-go/ldk_node" - // "github.com/getAlby/nostr-wallet-connect/ldk_node" - "github.com/getAlby/nostr-wallet-connect/logger" + // "github.com/getAlby/hub/ldk_node" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index 9c060c7b..855fe0d8 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -16,9 +16,9 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" decodepay "github.com/nbd-wtf/ln-decodepay" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/lnclient/lnd/wrapper" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/lnclient/lnd/wrapper" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" // "gorm.io/gorm" diff --git a/lnclient/phoenixd/phoenixd.go b/lnclient/phoenixd/phoenixd.go index a1820813..b40f5a1c 100644 --- a/lnclient/phoenixd/phoenixd.go +++ b/lnclient/phoenixd/phoenixd.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/main_wails.go b/main_wails.go index 143fb644..0b021d42 100644 --- a/main_wails.go +++ b/main_wails.go @@ -7,9 +7,9 @@ import ( "context" "embed" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" - "github.com/getAlby/nostr-wallet-connect/wails" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" + "github.com/getAlby/hub/wails" log "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/decode_request.go b/nip47/controllers/decode_request.go index 2fba4c07..0daedc92 100644 --- a/nip47/controllers/decode_request.go +++ b/nip47/controllers/decode_request.go @@ -3,8 +3,8 @@ package controllers import ( "encoding/json" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_balance_controller.go b/nip47/controllers/get_balance_controller.go index a9a4f888..d1c3172b 100644 --- a/nip47/controllers/get_balance_controller.go +++ b/nip47/controllers/get_balance_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_balance_controller_test.go b/nip47/controllers/get_balance_controller_test.go index a2ab671f..65bd3378 100644 --- a/nip47/controllers/get_balance_controller_test.go +++ b/nip47/controllers/get_balance_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47GetBalanceJson = ` diff --git a/nip47/controllers/get_info_controller.go b/nip47/controllers/get_info_controller.go index 2073e500..7e4e69ea 100644 --- a/nip47/controllers/get_info_controller.go +++ b/nip47/controllers/get_info_controller.go @@ -3,11 +3,11 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + permissions "github.com/getAlby/hub/nip47/permissions" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_info_controller_test.go b/nip47/controllers/get_info_controller_test.go index 835dc360..26c66759 100644 --- a/nip47/controllers/get_info_controller_test.go +++ b/nip47/controllers/get_info_controller_test.go @@ -8,10 +8,10 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/tests" ) const nip47GetInfoJson = ` diff --git a/nip47/controllers/list_transactions_controller.go b/nip47/controllers/list_transactions_controller.go index e5d7c261..914e28d1 100644 --- a/nip47/controllers/list_transactions_controller.go +++ b/nip47/controllers/list_transactions_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/list_transactions_controller_test.go b/nip47/controllers/list_transactions_controller_test.go index b838b73d..dfb61857 100644 --- a/nip47/controllers/list_transactions_controller_test.go +++ b/nip47/controllers/list_transactions_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47ListTransactionsJson = ` diff --git a/nip47/controllers/lookup_invoice_controller.go b/nip47/controllers/lookup_invoice_controller.go index 924d127f..de932438 100644 --- a/nip47/controllers/lookup_invoice_controller.go +++ b/nip47/controllers/lookup_invoice_controller.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/lookup_invoice_controller_test.go b/nip47/controllers/lookup_invoice_controller_test.go index 2434b6c2..9d8b396a 100644 --- a/nip47/controllers/lookup_invoice_controller_test.go +++ b/nip47/controllers/lookup_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47LookupInvoiceJson = ` diff --git a/nip47/controllers/make_invoice_controller.go b/nip47/controllers/make_invoice_controller.go index 7998b936..29d2746a 100644 --- a/nip47/controllers/make_invoice_controller.go +++ b/nip47/controllers/make_invoice_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/make_invoice_controller_test.go b/nip47/controllers/make_invoice_controller_test.go index fc7fb436..2e4dd6e0 100644 --- a/nip47/controllers/make_invoice_controller_test.go +++ b/nip47/controllers/make_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MakeInvoiceJson = ` diff --git a/nip47/controllers/models.go b/nip47/controllers/models.go index 7cc6ef60..9ccec5c3 100644 --- a/nip47/controllers/models.go +++ b/nip47/controllers/models.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" ) diff --git a/nip47/controllers/multi_pay_invoice_controller.go b/nip47/controllers/multi_pay_invoice_controller.go index 90b2c92c..f5736a44 100644 --- a/nip47/controllers/multi_pay_invoice_controller.go +++ b/nip47/controllers/multi_pay_invoice_controller.go @@ -6,11 +6,11 @@ import ( "strings" "sync" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/multi_pay_invoice_controller_test.go b/nip47/controllers/multi_pay_invoice_controller_test.go index 651484e4..ad5aeaf0 100644 --- a/nip47/controllers/multi_pay_invoice_controller_test.go +++ b/nip47/controllers/multi_pay_invoice_controller_test.go @@ -9,9 +9,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MultiPayJson = ` diff --git a/nip47/controllers/multi_pay_keysend_controller.go b/nip47/controllers/multi_pay_keysend_controller.go index 29d9da58..7f5bb225 100644 --- a/nip47/controllers/multi_pay_keysend_controller.go +++ b/nip47/controllers/multi_pay_keysend_controller.go @@ -4,10 +4,10 @@ import ( "context" "sync" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/nip47/controllers/multi_pay_keysend_controller_test.go b/nip47/controllers/multi_pay_keysend_controller_test.go index e6279433..a1bab5a4 100644 --- a/nip47/controllers/multi_pay_keysend_controller_test.go +++ b/nip47/controllers/multi_pay_keysend_controller_test.go @@ -9,9 +9,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MultiPayKeysendJson = ` diff --git a/nip47/controllers/pay_invoice_controller.go b/nip47/controllers/pay_invoice_controller.go index 1d3aa0c3..6a569e06 100644 --- a/nip47/controllers/pay_invoice_controller.go +++ b/nip47/controllers/pay_invoice_controller.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/pay_invoice_controller_test.go b/nip47/controllers/pay_invoice_controller_test.go index 92b17502..683a3344 100644 --- a/nip47/controllers/pay_invoice_controller_test.go +++ b/nip47/controllers/pay_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47PayInvoiceJson = ` diff --git a/nip47/controllers/pay_keysend_controller.go b/nip47/controllers/pay_keysend_controller.go index 734f3794..6aab0d29 100644 --- a/nip47/controllers/pay_keysend_controller.go +++ b/nip47/controllers/pay_keysend_controller.go @@ -3,11 +3,11 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" "gorm.io/gorm" diff --git a/nip47/controllers/pay_keysend_controller_test.go b/nip47/controllers/pay_keysend_controller_test.go index d973e81a..4bd72420 100644 --- a/nip47/controllers/pay_keysend_controller_test.go +++ b/nip47/controllers/pay_keysend_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47KeysendJson = ` diff --git a/nip47/controllers/sign_message_controller.go b/nip47/controllers/sign_message_controller.go index 374ad633..10ea0ed2 100644 --- a/nip47/controllers/sign_message_controller.go +++ b/nip47/controllers/sign_message_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/event_handler.go b/nip47/event_handler.go index e606a655..c84cbb62 100644 --- a/nip47/event_handler.go +++ b/nip47/event_handler.go @@ -7,13 +7,13 @@ import ( "fmt" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - controllers "github.com/getAlby/nostr-wallet-connect/nip47/controllers" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + controllers "github.com/getAlby/hub/nip47/controllers" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/sirupsen/logrus" diff --git a/nip47/event_handler_test.go b/nip47/event_handler_test.go index b1e874f6..2ce7545a 100644 --- a/nip47/event_handler_test.go +++ b/nip47/event_handler_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/stretchr/testify/assert" diff --git a/nip47/models/models.go b/nip47/models/models.go index c74d49e5..524a0aad 100644 --- a/nip47/models/models.go +++ b/nip47/models/models.go @@ -3,7 +3,7 @@ package models import ( "encoding/json" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) const ( diff --git a/nip47/nip47_service.go b/nip47/nip47_service.go index fee56a5b..aa365bbb 100644 --- a/nip47/nip47_service.go +++ b/nip47/nip47_service.go @@ -3,12 +3,12 @@ package nip47 import ( "context" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/notifications" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/notifications" + permissions "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service/keys" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/nip47/notifications/models.go b/nip47/notifications/models.go index 22b416c8..888c81d8 100644 --- a/nip47/notifications/models.go +++ b/nip47/notifications/models.go @@ -1,6 +1,6 @@ package notifications -import "github.com/getAlby/nostr-wallet-connect/nip47/models" +import "github.com/getAlby/hub/nip47/models" type Notification struct { Notification interface{} `json:"notification,omitempty"` diff --git a/nip47/notifications/nip47_notification_queue.go b/nip47/notifications/nip47_notification_queue.go index 59bf954b..e5bb5b4b 100644 --- a/nip47/notifications/nip47_notification_queue.go +++ b/nip47/notifications/nip47_notification_queue.go @@ -4,8 +4,8 @@ import ( "context" "errors" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" ) type Nip47NotificationQueue interface { diff --git a/nip47/notifications/nip47_notifier.go b/nip47/notifications/nip47_notifier.go index 49b36b8a..e0e7dbd4 100644 --- a/nip47/notifications/nip47_notifier.go +++ b/nip47/notifications/nip47_notifier.go @@ -5,14 +5,14 @@ import ( "encoding/json" "errors" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service/keys" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/sirupsen/logrus" diff --git a/nip47/notifications/nip47_notifier_test.go b/nip47/notifications/nip47_notifier_test.go index 08bc9e1b..e35b31f2 100644 --- a/nip47/notifications/nip47_notifier_test.go +++ b/nip47/notifications/nip47_notifier_test.go @@ -6,10 +6,10 @@ import ( "log" "testing" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/tests" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/stretchr/testify/assert" diff --git a/nip47/permissions/permissions.go b/nip47/permissions/permissions.go index 523f828f..7040c5f4 100644 --- a/nip47/permissions/permissions.go +++ b/nip47/permissions/permissions.go @@ -5,12 +5,12 @@ import ( "slices" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/utils" "github.com/sirupsen/logrus" "gorm.io/gorm" ) diff --git a/nip47/permissions/permissions_test.go b/nip47/permissions/permissions_test.go index 7e53146b..a5224bcc 100644 --- a/nip47/permissions/permissions_test.go +++ b/nip47/permissions/permissions_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" "github.com/stretchr/testify/assert" ) diff --git a/nip47/publish_nip47_info.go b/nip47/publish_nip47_info.go index bb877a9e..fe7877ae 100644 --- a/nip47/publish_nip47_info.go +++ b/nip47/publish_nip47_info.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" ) diff --git a/render.yaml b/render.yaml index d6f09e8b..10087372 100644 --- a/render.yaml +++ b/render.yaml @@ -3,7 +3,7 @@ services: runtime: image name: albyhub image: - url: ghcr.io/getalby/nostr-wallet-connect-next:latest + url: ghcr.io/getalby/hub:latest numInstances: 1 region: frankfurt # Default: oregon plan: starter diff --git a/scripts/README.md b/scripts/README.md index 781859b7..5453f825 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,11 +1,11 @@ ### Installation on a Raspberry Pi Zero ```shell -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/install.sh)" +/bin/bash -c "$(curl -fsSL https://getalby.com/zero/install.sh)" ``` ### Updating a running instance ```shell -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/update.sh)" +/bin/bash -c "$(curl -fsSL https://getalby.com/zero/update.sh)" ``` diff --git a/scripts/linux-x86_64/phoenixd/README.md b/scripts/linux-x86_64/phoenixd/README.md index f1472322..79ecddc0 100644 --- a/scripts/linux-x86_64/phoenixd/README.md +++ b/scripts/linux-x86_64/phoenixd/README.md @@ -4,14 +4,14 @@ Run your Alby Hub with phoenixd as a backend. ## Requirements -+ Linux distribution -+ Runs pretty much on any VPS or server +- Linux distribution +- Runs pretty much on any VPS or server ## Docker -To run Alby Hub with phoenixd use [docker-compose](https://docs.docker.com/compose/) using the [docker-compose.yml file](https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/docker-compose.yml). +To run Alby Hub with phoenixd use [docker-compose](https://docs.docker.com/compose/) using the [docker-compose.yml file](https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/docker-compose.yml). - $ wget https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/docker-compose.yml + $ wget https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/docker-compose.yml $ docker-compose up # or docker-compose up --pull=always <- to make sure you get the latest images It will run on localhost:8080 by default. You can configure the port by editing the docker-compose.yml file. @@ -26,7 +26,7 @@ Make sure to backup the `albyhub-phoenixd` which is used as volume for albyhub a ### Installation (non-Docker) - $ wget https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/install.sh + $ wget https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/install.sh $ ./install.sh The install script will prompt you for a installation folder and will install phoenixd and Alby Hub there. @@ -47,10 +47,9 @@ Or us the start scripts: $ [your install path]/phoenixd/start.sh $ [your install path]/albyhub/start.sh - ### Backup Make sure to backup your data directories: -+ `[your install path]/phoenixd/data` -+ `[your install path]/albyhub/data` +- `[your install path]/phoenixd/data` +- `[your install path]/albyhub/data` diff --git a/scripts/linux-x86_64/phoenixd/docker-compose.yml b/scripts/linux-x86_64/phoenixd/docker-compose.yml index 794e03b9..9f7b5ef0 100644 --- a/scripts/linux-x86_64/phoenixd/docker-compose.yml +++ b/scripts/linux-x86_64/phoenixd/docker-compose.yml @@ -2,7 +2,7 @@ services: albyhub: platform: linux/amd64 container_name: albyhub - image: ghcr.io/getalby/nostr-wallet-connect-next:latest + image: ghcr.io/getalby/hub:latest volumes: - ./albyhub-phoenixd:/data ports: diff --git a/service/keys/keys.go b/service/keys/keys.go index 7135db4d..cde80dd9 100644 --- a/service/keys/keys.go +++ b/service/keys/keys.go @@ -1,8 +1,8 @@ package keys import ( - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/logger" "github.com/nbd-wtf/go-nostr" ) diff --git a/service/models.go b/service/models.go index 8bdb7c3f..bd056b53 100644 --- a/service/models.go +++ b/service/models.go @@ -1,11 +1,11 @@ package service import ( - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/service/keys" "gorm.io/gorm" ) diff --git a/service/service.go b/service/service.go index f1e30ad2..63a4af84 100644 --- a/service/service.go +++ b/service/service.go @@ -16,17 +16,17 @@ import ( "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service/keys" - "github.com/getAlby/nostr-wallet-connect/version" - - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service/keys" + "github.com/getAlby/hub/version" + + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47" + "github.com/getAlby/hub/nip47/models" ) type service struct { @@ -56,7 +56,7 @@ func NewService(ctx context.Context) (*service, error) { logger.Logger.Info("AlbyHub " + version.Tag) if appConfig.Workdir == "" { - appConfig.Workdir = filepath.Join(xdg.DataHome, "/alby-nwc") + appConfig.Workdir = filepath.Join(xdg.DataHome, "/albyhub") logger.Logger.WithField("workdir", appConfig.Workdir).Info("No workdir specified, using default") } // make sure workdir exists diff --git a/service/start.go b/service/start.go index 2f44774c..83715e41 100644 --- a/service/start.go +++ b/service/start.go @@ -10,16 +10,16 @@ import ( "github.com/nbd-wtf/go-nostr/nip19" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/lnclient/breez" - "github.com/getAlby/nostr-wallet-connect/lnclient/cashu" - "github.com/getAlby/nostr-wallet-connect/lnclient/greenlight" - "github.com/getAlby/nostr-wallet-connect/lnclient/ldk" - "github.com/getAlby/nostr-wallet-connect/lnclient/lnd" - "github.com/getAlby/nostr-wallet-connect/lnclient/phoenixd" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/lnclient/breez" + "github.com/getAlby/hub/lnclient/cashu" + "github.com/getAlby/hub/lnclient/greenlight" + "github.com/getAlby/hub/lnclient/ldk" + "github.com/getAlby/hub/lnclient/lnd" + "github.com/getAlby/hub/lnclient/phoenixd" + "github.com/getAlby/hub/logger" ) func (svc *service) StartNostr(ctx context.Context, encryptionKey string) error { @@ -39,7 +39,7 @@ func (svc *service) StartNostr(ctx context.Context, encryptionKey string) error logger.Logger.WithFields(logrus.Fields{ "npub": npub, "hex": svc.keys.GetNostrPublicKey(), - }).Info("Starting nostr-wallet-connect") + }).Info("Starting Alby Hub") svc.wg.Add(1) go func() { //Start infinite loop which will be only broken by canceling ctx (SIGINT) diff --git a/service/stop.go b/service/stop.go index cdff012c..2ec0fe03 100644 --- a/service/stop.go +++ b/service/stop.go @@ -3,8 +3,8 @@ package service import ( "fmt" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" ) // TODO: this should happen on ctx.Done() rather than having to call manually diff --git a/tests/create_app.go b/tests/create_app.go index 7acb676a..9a72ba10 100644 --- a/tests/create_app.go +++ b/tests/create_app.go @@ -1,7 +1,7 @@ package tests import ( - "github.com/getAlby/nostr-wallet-connect/db" + "github.com/getAlby/hub/db" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" ) diff --git a/tests/mock_ln_client.go b/tests/mock_ln_client.go index d7f5207e..b1e5d625 100644 --- a/tests/mock_ln_client.go +++ b/tests/mock_ln_client.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) // for the invoice: diff --git a/tests/test_service.go b/tests/test_service.go index 2d3c480a..ec3a5b6b 100644 --- a/tests/test_service.go +++ b/tests/test_service.go @@ -3,12 +3,12 @@ package tests import ( "os" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service/keys" "gorm.io/gorm" ) diff --git a/version/version.go b/version/version.go index e2c5b151..f8f49f47 100644 --- a/version/version.go +++ b/version/version.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) @@ -23,7 +23,7 @@ func GetLatestReleaseTag() string { if latestRelease != "" && time.Since(lastVersionCheck) < 5*time.Minute { return latestRelease } - url := "https://api.github.com/repos/getAlby/nostr-wallet-connect-next/releases" + url := "https://api.github.com/repos/getAlby/hub/releases" client := http.Client{ Timeout: time.Second * 10, diff --git a/wails.json b/wails.json index 924d8e75..bde8ff22 100644 --- a/wails.json +++ b/wails.json @@ -1,7 +1,7 @@ { "$schema": "https://wails.io/schemas/config.v2.json", - "name": "AlbyHub", - "outputfilename": "Nostr-Wallet-Connect", + "name": "Alby Hub", + "outputfilename": "albyhub", "frontend:install": "yarn install", "frontend:build": "yarn build:wails", "frontend:dev:watcher": "yarn dev:wails", diff --git a/wails/wails_app.go b/wails/wails_app.go index 001c109e..4425476e 100644 --- a/wails/wails_app.go +++ b/wails/wails_app.go @@ -5,9 +5,9 @@ import ( "embed" "log" - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go index 5b9f4109..298454c1 100644 --- a/wails/wails_handlers.go +++ b/wails/wails_handlers.go @@ -9,10 +9,10 @@ import ( "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" "github.com/wailsapp/wails/v2/pkg/runtime" ) From d16525985f6306d3022d6a3c059efda4e00b7a03 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 5 Jul 2024 21:00:15 +0700 Subject: [PATCH 18/47] fix: wails mac filename --- wails.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wails.json b/wails.json index bde8ff22..64b30e3c 100644 --- a/wails.json +++ b/wails.json @@ -1,6 +1,6 @@ { "$schema": "https://wails.io/schemas/config.v2.json", - "name": "Alby Hub", + "name": "albyhub", "outputfilename": "albyhub", "frontend:install": "yarn install", "frontend:build": "yarn build:wails", From 538c1dd884a4fdee7346f0152d9d6ddf257fa9a7 Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:26:20 +0700 Subject: [PATCH 19/47] Feat: include LSPS1 maximum channel size in frontend (#6) * feat: show maximum channel size for LSPS1 options * fix: lsps1 ok partner check to also check maximum channel size --- alby/models.go | 1 + .../channels/IncreaseIncomingCapacity.tsx | 23 +++++++++++-------- .../channels/IncreaseOutgoingCapacity.tsx | 1 + frontend/src/types.ts | 1 + 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/alby/models.go b/alby/models.go index 0392a93c..625f8b4e 100644 --- a/alby/models.go +++ b/alby/models.go @@ -52,6 +52,7 @@ type ChannelPeerSuggestion struct { Pubkey string `json:"pubkey"` Host string `json:"host"` MinimumChannelSize uint64 `json:"minimumChannelSize"` + MaximumChannelSize uint64 `json:"maximumChannelSize"` Name string `json:"name"` Image string `json:"image"` BrokenLspUrl string `json:"lsp_url"` diff --git a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx index fe55ea01..7296d8fd 100644 --- a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx @@ -128,6 +128,7 @@ function NewChannelInternal({ network }: { network: Network }) { const okPartners = channelPeerSuggestions.filter( (partner) => amount >= partner.minimumChannelSize && + amount <= partner.maximumChannelSize && partner.network === network && partner.paymentMethod === "lightning" && partner.lspType === "LSPS1" && @@ -264,15 +265,19 @@ function NewChannelInternal({ network }: { network: Network }) { )}
{peer.name} - {peer.minimumChannelSize > 0 && ( - - Min.{" "} - {new Intl.NumberFormat().format( - peer.minimumChannelSize - )}{" "} - sats - - )} + + Min.{" "} + {new Intl.NumberFormat().format( + peer.minimumChannelSize + )} + sats + + Max.{" "} + {new Intl.NumberFormat().format( + peer.maximumChannelSize + )}{" "} + sats +
diff --git a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx index 2fce2778..5e578b8e 100644 --- a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx @@ -70,6 +70,7 @@ function NewChannelInternal({ network }: { network: Network }) { network, paymentMethod: "onchain", minimumChannelSize: 0, + maximumChannelSize: 0, pubkey: "", host: "", image: "", diff --git a/frontend/src/types.ts b/frontend/src/types.ts index db6a5b80..b7a7abbd 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -298,6 +298,7 @@ export type RecommendedChannelPeer = { image: string; name: string; minimumChannelSize: number; + maximumChannelSize: number; } & ( | { paymentMethod: "onchain"; From da2bb3b2f687f0ae5cd9a16a36c496c1bfdea93e Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:41:16 +0700 Subject: [PATCH 20/47] Feat: node health indicator (#11) * feat: add node health indicator to liquidity page * chore: minor improvements to node health indicator --- frontend/src/components/ui/progress.tsx | 23 ++++++++- frontend/src/screens/channels/Channels.tsx | 55 ++++++++++++++++++++-- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx index 336ac732..f7bf8c8a 100644 --- a/frontend/src/components/ui/progress.tsx +++ b/frontend/src/components/ui/progress.tsx @@ -1,5 +1,5 @@ -import * as React from "react"; import * as ProgressPrimitive from "@radix-ui/react-progress"; +import * as React from "react"; import { cn } from "src/lib/utils"; @@ -23,4 +23,23 @@ const Progress = React.forwardRef< )); Progress.displayName = ProgressPrimitive.Root.displayName; -export { Progress }; +const CircleProgress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + {props.children ||
{`${value || 0}%`}
} +
+)); + +export { CircleProgress, Progress }; diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 9e356730..def7aca2 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -7,6 +7,7 @@ import { CopyIcon, ExternalLinkIcon, HandCoins, + Heart, Hotel, InfoIcon, MoreHorizontal, @@ -43,7 +44,7 @@ import { DropdownMenuTrigger, } from "src/components/ui/dropdown-menu.tsx"; import { LoadingButton } from "src/components/ui/loading-button.tsx"; -import { Progress } from "src/components/ui/progress.tsx"; +import { CircleProgress, Progress } from "src/components/ui/progress.tsx"; import { Table, TableBody, @@ -95,6 +96,8 @@ export default function Channels() { const [drainingAlbySharedFunds, setDrainingAlbySharedFunds] = React.useState(false); + const nodeHealth = channels ? getNodeHealth(channels) : 0; + // TODO: move to NWC backend const loadNodeStats = React.useCallback(async () => { if (!channels) { @@ -287,7 +290,7 @@ export default function Channels() { title="Liquidity" description="Manage your lightning node liquidity" contentRight={ - <> +
*/} - + + + + + + + + + Node health: {nodeHealth}% + + + +
} > @@ -797,3 +812,37 @@ export default function Channels() { ); } + +function getNodeHealth(channels: Channel[]) { + const totalChannelCapacitySats = channels + .map((channel) => (channel.localBalance + channel.remoteBalance) / 1000) + .reduce((a, b) => a + b, 0); + + const averageChannelBalance = + channels + .map((channel) => { + const totalBalance = channel.localBalance + channel.remoteBalance; + const expectedBalance = totalBalance / 2; + const actualBalance = + Math.min(channel.localBalance, channel.remoteBalance) / + expectedBalance; + return actualBalance; + }) + .reduce((a, b) => a + b, 0) / (channels.length || 1); + + const numUniqueChannelPartners = new Set( + channels.map((channel) => channel.remotePubkey) + ).size; + + let nodeHealth = Math.ceil( + Math.min(3, numUniqueChannelPartners) * + (100 / 3) * // 3 channels is great + (Math.min(totalChannelCapacitySats, 1_000_000) / 1_000_000) * // 1 million sats or more is great + averageChannelBalance // perfectly balanced is great! + ); + + // above calculation is a bit harsh + nodeHealth = Math.min(nodeHealth * 2, 100); + + return nodeHealth; +} From 439c145617fdd9fcad60f8eb750904f03a3c2b83 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Fri, 5 Jul 2024 20:13:53 +0530 Subject: [PATCH 21/47] fix: add proper handling of invoice (#12) --- frontend/src/screens/wallet/Send.tsx | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/frontend/src/screens/wallet/Send.tsx b/frontend/src/screens/wallet/Send.tsx index d64619b5..650bff83 100644 --- a/frontend/src/screens/wallet/Send.tsx +++ b/frontend/src/screens/wallet/Send.tsx @@ -17,8 +17,8 @@ import { Label } from "src/components/ui/label"; import { LoadingButton } from "src/components/ui/loading-button"; import { useToast } from "src/components/ui/use-toast"; import { useBalances } from "src/hooks/useBalances"; -import { useInfo } from "src/hooks/useInfo"; import { useCSRF } from "src/hooks/useCSRF"; +import { useInfo } from "src/hooks/useInfo"; import { copyToClipboard } from "src/lib/clipboard"; import { PayInvoiceResponse } from "src/types"; import { request } from "src/utils/request"; @@ -39,6 +39,20 @@ export default function Send() { return ; } + const handleContinue = () => { + try { + new Invoice({ pr: invoice }); + setShowConfirmation(true); + } catch (error) { + toast({ + variant: "destructive", + title: "Invalid Payment Request", + }); + console.error(error); + setInvoice(""); + } + }; + const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); try { @@ -47,7 +61,7 @@ export default function Send() { } setLoading(true); const payInvoiceResponse = await request( - `/api/payments/${invoice.trim()}`, + `/api/payments/${invoice}`, { method: "POST", headers: { @@ -69,6 +83,8 @@ export default function Send() { variant: "destructive", title: "Failed to send: " + e, }); + setInvoice(""); + setShowConfirmation(false); console.error(e); } setLoading(false); @@ -81,7 +97,7 @@ export default function Send() { const paste = async () => { const text = await navigator.clipboard.readText(); - setInvoice(text); + setInvoice(text.trim()); }; return ( @@ -175,7 +191,7 @@ export default function Send() { autoFocus placeholder="Enter an invoice" onChange={(e) => { - setInvoice(e.target.value); + setInvoice(e.target.value.trim()); }} />
-
From baecbd2602ece25d7f3ea73c64d408de32027b46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:44:45 +0700 Subject: [PATCH 22/47] build(deps): bump github.com/gorilla/sessions from 1.2.2 to 1.3.0 (#5) Bumps [github.com/gorilla/sessions](https://github.com/gorilla/sessions) from 1.2.2 to 1.3.0. - [Release notes](https://github.com/gorilla/sessions/releases) - [Commits](https://github.com/gorilla/sessions/compare/v1.2.2...v1.3.0) --- updated-dependencies: - dependency-name: github.com/gorilla/sessions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0c4688e7..e81a6bc5 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59 github.com/getAlby/ldk-node-go v0.0.0-20240624140557-d51c707f10d9 github.com/go-gormigrate/gormigrate/v2 v2.1.2 - github.com/gorilla/sessions v1.2.2 + github.com/gorilla/sessions v1.3.0 github.com/labstack/echo-contrib v0.17.1 github.com/labstack/echo/v4 v4.12.0 github.com/nbd-wtf/go-nostr v0.32.0 diff --git a/go.sum b/go.sum index 1921c22a..0f29372b 100644 --- a/go.sum +++ b/go.sum @@ -278,8 +278,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= -github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= +github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= From 5f57d9b00b2e124d33ede5531ce8bcdd0e4e9446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:45:45 +0700 Subject: [PATCH 23/47] build(deps): bump google.golang.org/grpc from 1.64.0 to 1.65.0 (#4) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.65.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.65.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index e81a6bc5..73ef705a 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/wailsapp/wails/v2 v2.8.2 golang.org/x/crypto v0.24.0 golang.org/x/oauth2 v0.21.0 - google.golang.org/grpc v1.64.0 + google.golang.org/grpc v1.65.0 gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 gopkg.in/macaroon.v2 v2.1.0 gorm.io/gorm v1.25.10 @@ -205,8 +205,8 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/macaroon-bakery.v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 0f29372b..c4e4a95e 100644 --- a/go.sum +++ b/go.sum @@ -891,18 +891,18 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 h1:mMix4feEsbn2/wONR8e68JLob2QSdpiAMINhpG/8s7k= gopkg.in/DataDog/dd-trace-go.v1 v1.65.0/go.mod h1:beNFIWd/H04d0k96cfltgiDH2+t0T5sDbyYLF3VTXqk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 64e734fb9328845dd658bc201e11269ef1868d2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:55:24 +0700 Subject: [PATCH 24/47] build(deps): bump gopkg.in/DataDog/dd-trace-go.v1 from 1.65.0 to 1.65.1 (#3) Bumps gopkg.in/DataDog/dd-trace-go.v1 from 1.65.0 to 1.65.1. --- updated-dependencies: - dependency-name: gopkg.in/DataDog/dd-trace-go.v1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 73ef705a..2b564173 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( golang.org/x/crypto v0.24.0 golang.org/x/oauth2 v0.21.0 google.golang.org/grpc v1.65.0 - gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 + gopkg.in/DataDog/dd-trace-go.v1 v1.65.1 gopkg.in/macaroon.v2 v2.1.0 gorm.io/gorm v1.25.10 ) diff --git a/go.sum b/go.sum index c4e4a95e..ff64021c 100644 --- a/go.sum +++ b/go.sum @@ -903,8 +903,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 h1:mMix4feEsbn2/wONR8e68JLob2QSdpiAMINhpG/8s7k= -gopkg.in/DataDog/dd-trace-go.v1 v1.65.0/go.mod h1:beNFIWd/H04d0k96cfltgiDH2+t0T5sDbyYLF3VTXqk= +gopkg.in/DataDog/dd-trace-go.v1 v1.65.1 h1:Ne7kzWr/br/jwhUJR7CnqPl/mUpNxa6LfgZs0S4htZM= +gopkg.in/DataDog/dd-trace-go.v1 v1.65.1/go.mod h1:beNFIWd/H04d0k96cfltgiDH2+t0T5sDbyYLF3VTXqk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From e5b5f58b67692cdb036135d2d9681cef8cde019e Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:57:33 +0700 Subject: [PATCH 25/47] feat: rename liquidity page to node (#218) --- frontend/src/components/layouts/AppLayout.tsx | 6 +++--- frontend/src/routes.tsx | 2 +- frontend/src/screens/channels/Channels.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/layouts/AppLayout.tsx b/frontend/src/components/layouts/AppLayout.tsx index 788efed1..fac4baf1 100644 --- a/frontend/src/components/layouts/AppLayout.tsx +++ b/frontend/src/components/layouts/AppLayout.tsx @@ -2,7 +2,6 @@ import { Cable, EllipsisVertical, ExternalLinkIcon, - FlaskRound, Home, Lock, Megaphone, @@ -15,6 +14,7 @@ import { Wallet, } from "lucide-react"; +import { CubeIcon } from "@radix-ui/react-icons"; import React from "react"; import { Link, @@ -142,8 +142,8 @@ export default function AppLayout() {
diff --git a/frontend/src/components/ui/credenza.tsx b/frontend/src/components/ui/credenza.tsx new file mode 100644 index 00000000..587515fd --- /dev/null +++ b/frontend/src/components/ui/credenza.tsx @@ -0,0 +1,148 @@ +import * as React from "react"; + +import { useMediaQuery } from "src/hooks/useMediaQuery"; +import { cn } from "src/lib/utils"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./dialog"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "./drawer"; + +interface BaseProps { + children: React.ReactNode; +} + +interface RootCredenzaProps extends BaseProps { + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +interface CredenzaProps extends BaseProps { + className?: string; + asChild?: true; +} + +const desktop = "(min-width: 768px)"; + +const Credenza = ({ children, ...props }: RootCredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const Credenza = isDesktop ? Dialog : Drawer; + + return {children}; +}; + +const CredenzaTrigger = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaTrigger = isDesktop ? DialogTrigger : DrawerTrigger; + + return ( + + {children} + + ); +}; + +const CredenzaClose = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaClose = isDesktop ? DialogClose : DrawerClose; + + return ( + + {children} + + ); +}; + +const CredenzaContent = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaContent = isDesktop ? DialogContent : DrawerContent; + + return ( + + {children} + + ); +}; + +const CredenzaDescription = ({ + className, + children, + ...props +}: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaDescription = isDesktop ? DialogDescription : DrawerDescription; + + return ( + + {children} + + ); +}; + +const CredenzaHeader = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaHeader = isDesktop ? DialogHeader : DrawerHeader; + + return ( + + {children} + + ); +}; + +const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaTitle = isDesktop ? DialogTitle : DrawerTitle; + + return ( + + {children} + + ); +}; + +const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => { + return ( +
+ {children} +
+ ); +}; + +const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaFooter = isDesktop ? DialogFooter : DrawerFooter; + + return ( + + {children} + + ); +}; + +export { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle, + CredenzaTrigger, +}; diff --git a/frontend/src/components/ui/drawer.tsx b/frontend/src/components/ui/drawer.tsx new file mode 100644 index 00000000..36f39019 --- /dev/null +++ b/frontend/src/components/ui/drawer.tsx @@ -0,0 +1,116 @@ +import * as React from "react"; +import { Drawer as DrawerPrimitive } from "vaul"; + +import { cn } from "src/lib/utils"; + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +); +Drawer.displayName = "Drawer"; + +const DrawerTrigger = DrawerPrimitive.Trigger; + +const DrawerPortal = DrawerPrimitive.Portal; + +const DrawerClose = DrawerPrimitive.Close; + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
+ {children} + + +)); +DrawerContent.displayName = "DrawerContent"; + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerHeader.displayName = "DrawerHeader"; + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerFooter.displayName = "DrawerFooter"; + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerTitle.displayName = DrawerPrimitive.Title.displayName; + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerDescription.displayName = DrawerPrimitive.Description.displayName; + +export { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerOverlay, + DrawerPortal, + DrawerTitle, + DrawerTrigger, +}; diff --git a/frontend/src/hooks/useMediaQuery.ts b/frontend/src/hooks/useMediaQuery.ts new file mode 100644 index 00000000..48db609e --- /dev/null +++ b/frontend/src/hooks/useMediaQuery.ts @@ -0,0 +1,19 @@ +import * as React from "react"; + +export function useMediaQuery(query: string) { + const [value, setValue] = React.useState(window.matchMedia(query).matches); + + React.useEffect(() => { + function onChange(event: MediaQueryListEvent) { + setValue(event.matches); + } + + const result = matchMedia(query); + result.addEventListener("change", onChange); + setValue(result.matches); + + return () => result.removeEventListener("change", onChange); + }, [query]); + + return value; +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 06ca7b01..b70995dd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -529,6 +529,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/primitive@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" + integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== + "@radix-ui/react-alert-dialog@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz#70dd529cbf1e4bff386814d3776901fcaa131b8c" @@ -594,6 +599,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-compose-refs@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" + integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw== + "@radix-ui/react-context@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" @@ -601,6 +611,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-context@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" + integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== + "@radix-ui/react-dialog@1.0.5", "@radix-ui/react-dialog@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300" @@ -622,6 +637,26 @@ aria-hidden "^1.1.1" react-remove-scroll "2.5.5" +"@radix-ui/react-dialog@^1.0.4": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz#4906507f7b4ad31e22d7dad69d9330c87c431d44" + integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-dismissable-layer" "1.1.0" + "@radix-ui/react-focus-guards" "1.1.0" + "@radix-ui/react-focus-scope" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-portal" "1.1.1" + "@radix-ui/react-presence" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.7" + "@radix-ui/react-direction@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b" @@ -641,6 +676,17 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-escape-keydown" "1.0.3" +"@radix-ui/react-dismissable-layer@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz#2cd0a49a732372513733754e6032d3fb7988834e" + integrity sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/react-dropdown-menu@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz#cdf13c956c5e263afe4e5f3587b3071a25755b63" @@ -662,6 +708,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-focus-guards@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13" + integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw== + "@radix-ui/react-focus-scope@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz#2ac45fce8c5bb33eb18419cdc1905ef4f1906525" @@ -672,6 +723,15 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-focus-scope@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2" + integrity sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-icons@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.0.tgz#c61af8f323d87682c5ca76b856d60c2312dbcb69" @@ -685,6 +745,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-id@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" + integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-label@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.0.2.tgz#9c72f1d334aac996fdc27b48a8bdddd82108fb6d" @@ -764,6 +831,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-portal@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.1.tgz#1957f1eb2e1aedfb4a5475bd6867d67b50b1d15f" + integrity sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-presence@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba" @@ -773,6 +848,14 @@ "@radix-ui/react-compose-refs" "1.0.1" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-presence@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.0.tgz#227d84d20ca6bfe7da97104b1a8b48a833bfb478" + integrity sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-primitive@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" @@ -781,6 +864,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-primitive@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" + integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw== + dependencies: + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-progress@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-progress/-/react-progress-1.0.3.tgz#8380272fdc64f15cbf263a294dea70a7d5d9b4fa" @@ -850,6 +940,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" +"@radix-ui/react-slot@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" + integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-switch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.0.3.tgz#6119f16656a9eafb4424c600fdb36efa5ec5837e" @@ -909,6 +1006,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-callback-ref@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" + integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== + "@radix-ui/react-use-controllable-state@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" @@ -917,6 +1019,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-controllable-state@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" + integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755" @@ -925,6 +1034,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-escape-keydown@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" + integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" @@ -932,6 +1048,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-layout-effect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" + integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== + "@radix-ui/react-use-previous@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" @@ -2935,7 +3056,7 @@ react-qr-code@^2.0.12: prop-types "^15.8.1" qr.js "0.0.0" -react-remove-scroll-bar@^2.3.3: +react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4: version "2.3.6" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c" integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g== @@ -2954,6 +3075,17 @@ react-remove-scroll@2.5.5: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-remove-scroll@2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb" + integrity sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA== + dependencies: + react-remove-scroll-bar "^2.3.4" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + react-router-dom@^6.21.0: version "6.21.0" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.0.tgz" @@ -3409,6 +3541,13 @@ util-deprecate@^1.0.2: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +vaul@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/vaul/-/vaul-0.9.1.tgz#3640198e04636b209b1f907fcf3079bec6ecc66b" + integrity sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw== + dependencies: + "@radix-ui/react-dialog" "^1.0.4" + vite-tsconfig-paths@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9" diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index 14da8edf..de9ca219 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -727,7 +727,6 @@ func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *lnclient.Transaction { Preimage: preimage, PaymentHash: hex.EncodeToString(invoice.RHash), Amount: invoice.ValueMsat, - FeesPaid: invoice.AmtPaidMsat, CreatedAt: invoice.CreationDate, SettledAt: settledAt, ExpiresAt: expiresAt, From 6df9405853cbc354788214ad95ccf2a37e94631f Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:26:38 +0700 Subject: [PATCH 34/47] fix: reduce weight of channel balance metric to 10% in node health calculation (#240) * fix: reduce weight of channel balance metric to 10% in node health calculation * feat: change node heart indicator style when health is 100% * fix: node health indicator style * chore: increase heart indicator stroke width * chore: improve node health calculation * fix: return 100 node health if health is above 95 --- frontend/src/screens/channels/Channels.tsx | 36 ++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 842f24d1..2cf72c19 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -370,8 +370,25 @@ export default function Channels() { - - + + {nodeHealth === 100 && ( +
+
+
+ )} + Node health: {nodeHealth}% @@ -817,7 +834,6 @@ function getNodeHealth(channels: Channel[]) { const totalChannelCapacitySats = channels .map((channel) => (channel.localBalance + channel.remoteBalance) / 1000) .reduce((a, b) => a + b, 0); - const averageChannelBalance = channels .map((channel) => { @@ -834,15 +850,17 @@ function getNodeHealth(channels: Channel[]) { channels.map((channel) => channel.remotePubkey) ).size; - let nodeHealth = Math.ceil( - Math.min(3, numUniqueChannelPartners) * - (100 / 3) * // 3 channels is great + const nodeHealth = Math.ceil( + numUniqueChannelPartners * + (100 / 2) * // 2 or more channels is great (Math.min(totalChannelCapacitySats, 1_000_000) / 1_000_000) * // 1 million sats or more is great - averageChannelBalance // perfectly balanced is great! + (0.9 + averageChannelBalance * 0.1) // +10% for perfectly balanced channels ); - // above calculation is a bit harsh - nodeHealth = Math.min(nodeHealth * 2, 100); + if (nodeHealth > 95) { + // prevent OCD + return 100; + } return nodeHealth; } From 133184796f42789912a0c5e078b0fae9c6a72eb7 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Wed, 10 Jul 2024 08:31:12 +0300 Subject: [PATCH 35/47] chore: descriptive class names and mark sensitive content (#239) * chore: descriptive class names and mark sensitive content * fix: typo --------- Co-authored-by: Roland Bewick --- frontend/src/components/MnemonicInputs.tsx | 2 +- frontend/src/components/TransactionItem.tsx | 2 +- frontend/src/components/TransactionsList.tsx | 2 +- frontend/src/components/connections/AppCard.tsx | 2 +- frontend/src/screens/apps/AppCreated.tsx | 2 +- frontend/src/screens/apps/AppList.tsx | 2 +- frontend/src/screens/channels/Channels.tsx | 10 +++++----- frontend/src/screens/wallet/Send.tsx | 2 +- frontend/src/screens/wallet/index.tsx | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/MnemonicInputs.tsx b/frontend/src/components/MnemonicInputs.tsx index bab01c8e..0d0d39a0 100644 --- a/frontend/src/components/MnemonicInputs.tsx +++ b/frontend/src/components/MnemonicInputs.tsx @@ -40,7 +40,7 @@ export default function MnemonicInputs({ Recovery phrase to your wallet -
+
{words.map((word, i) => { const isRevealed = revealedIndex === i; const inputId = `mnemonic-word-${i}`; diff --git a/frontend/src/components/TransactionItem.tsx b/frontend/src/components/TransactionItem.tsx index f51cdab8..1eae0234 100644 --- a/frontend/src/components/TransactionItem.tsx +++ b/frontend/src/components/TransactionItem.tsx @@ -50,7 +50,7 @@ function TransactionItem({ tx }: Props) { >
diff --git a/frontend/src/components/TransactionsList.tsx b/frontend/src/components/TransactionsList.tsx index 41bb28ff..0bc7ba0e 100644 --- a/frontend/src/components/TransactionsList.tsx +++ b/frontend/src/components/TransactionsList.tsx @@ -12,7 +12,7 @@ function TransactionsList() { } return ( -
+
{!transactions?.length ? ( - + diff --git a/frontend/src/screens/apps/AppCreated.tsx b/frontend/src/screens/apps/AppCreated.tsx index 640ef997..44bd88eb 100644 --- a/frontend/src/screens/apps/AppCreated.tsx +++ b/frontend/src/screens/apps/AppCreated.tsx @@ -89,7 +89,7 @@ export default function AppCreated() { title={`Connect to ${createAppResponse.name}`} description="Configure wallet permissions for the app and follow instructions to finalise the connection" /> -
+

1. Open{" "} diff --git a/frontend/src/screens/apps/AppList.tsx b/frontend/src/screens/apps/AppList.tsx index 8d417669..cc4e8270 100644 --- a/frontend/src/screens/apps/AppList.tsx +++ b/frontend/src/screens/apps/AppList.tsx @@ -52,7 +52,7 @@ function AppList() { )} {otherApps.length > 0 && ( -

+
{otherApps.map((app, index) => ( ))} diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 2cf72c19..8f4c3f03 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -523,7 +523,7 @@ export default function Channels() {
)} -
+
{balances && ( <> {new Intl.NumberFormat().format(balances.onchain.spendable)}{" "} @@ -567,7 +567,7 @@ export default function Channels() {
)} {balances && ( -
+
{new Intl.NumberFormat(undefined, {}).format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} @@ -589,7 +589,7 @@ export default function Channels() { -
+
{balances && new Intl.NumberFormat().format( Math.floor(balances.lightning.totalReceivable / 1000) @@ -617,7 +617,7 @@ export default function Channels() { {!channels || (channels.length > 0 && ( - +
Status @@ -680,7 +680,7 @@ export default function Channels() { } return ( - + {channel.active ? ( Online diff --git a/frontend/src/screens/wallet/Send.tsx b/frontend/src/screens/wallet/Send.tsx index 650bff83..d6f10d4f 100644 --- a/frontend/src/screens/wallet/Send.tsx +++ b/frontend/src/screens/wallet/Send.tsx @@ -228,7 +228,7 @@ export default function Send() { )} {balances && ( -
+
{new Intl.NumberFormat(undefined, {}).format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} diff --git a/frontend/src/screens/wallet/index.tsx b/frontend/src/screens/wallet/index.tsx index 4f733e31..eb35a6d8 100644 --- a/frontend/src/screens/wallet/index.tsx +++ b/frontend/src/screens/wallet/index.tsx @@ -22,7 +22,7 @@ function Wallet() {
-
+
{new Intl.NumberFormat().format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} From eb0a97a8b1299afd7b0a5b33e7752b2057a70bf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:31:42 +0700 Subject: [PATCH 36/47] build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 (#228) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/crypto/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 5863100e..c259cd13 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/orandin/lumberjackrus v1.0.1 github.com/stretchr/testify v1.9.0 github.com/wailsapp/wails/v2 v2.8.2 - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.25.0 golang.org/x/oauth2 v0.21.0 google.golang.org/grpc v1.65.0 gopkg.in/DataDog/dd-trace-go.v1 v1.65.1 @@ -199,8 +199,8 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect diff --git a/go.sum b/go.sum index 687c528c..73dbcf07 100644 --- a/go.sum +++ b/go.sum @@ -739,8 +739,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= @@ -837,13 +837,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 894c2c3350179d740f79907e1c4ce33389a690c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:51:22 +0700 Subject: [PATCH 37/47] build(deps): bump github.com/wailsapp/wails/v2 from 2.8.2 to 2.9.1 (#229) * build(deps): bump github.com/wailsapp/wails/v2 from 2.8.2 to 2.9.1 Bumps [github.com/wailsapp/wails/v2](https://github.com/wailsapp/wails) from 2.8.2 to 2.9.1. - [Release notes](https://github.com/wailsapp/wails/releases) - [Commits](https://github.com/wailsapp/wails/compare/v2.8.2...v2.9.1) --- updated-dependencies: - dependency-name: github.com/wailsapp/wails/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: bump wails workflow cli version --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Roland Bewick --- .github/workflows/wails.yml | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wails.yml b/.github/workflows/wails.yml index 23d10827..e19c6967 100644 --- a/.github/workflows/wails.yml +++ b/.github/workflows/wails.yml @@ -65,7 +65,7 @@ jobs: node-version: "20.x" - name: Install Wails - run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.2 + run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.9.1 shell: bash - name: Install Linux Wails deps diff --git a/go.mod b/go.mod index c259cd13..ba350e9e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/nbd-wtf/ln-decodepay v1.12.1 github.com/orandin/lumberjackrus v1.0.1 github.com/stretchr/testify v1.9.0 - github.com/wailsapp/wails/v2 v2.8.2 + github.com/wailsapp/wails/v2 v2.9.1 golang.org/x/crypto v0.25.0 golang.org/x/oauth2 v0.21.0 google.golang.org/grpc v1.65.0 diff --git a/go.sum b/go.sum index 73dbcf07..f7ad1606 100644 --- a/go.sum +++ b/go.sum @@ -649,8 +649,8 @@ github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhy github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.8.2 h1:rYOn9p+7bJiZuFSi2wDyq8rBLHrIX/FoUxov+RpdUOI= -github.com/wailsapp/wails/v2 v2.8.2/go.mod h1:5pTURIST4yZ/wRcmqDUtnM0Mk+caNax/oS610hFiy74= +github.com/wailsapp/wails/v2 v2.9.1 h1:irsXnoQrCpeKzKTYZ2SUVlRRyeMR6I0vCO9Q1cvlEdc= +github.com/wailsapp/wails/v2 v2.9.1/go.mod h1:7maJV2h+Egl11Ak8QZN/jlGLj2wg05bsQS+ywJPT0gI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From fe24b2c716e3230a80e247877c9a0ec18cb826fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Wed, 10 Jul 2024 10:21:17 +0200 Subject: [PATCH 38/47] fix: make copy to clipboard work in non-https environments --- frontend/src/lib/clipboard.ts | 15 ++++----------- frontend/src/screens/apps/AppCreated.tsx | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/frontend/src/lib/clipboard.ts b/frontend/src/lib/clipboard.ts index a35baa50..b7f16bdb 100644 --- a/frontend/src/lib/clipboard.ts +++ b/frontend/src/lib/clipboard.ts @@ -11,12 +11,15 @@ export async function copyToClipboard(content: string) { textArea.style.position = "absolute"; textArea.style.opacity = "0"; document.body.appendChild(textArea); - selectElement(textArea); + textArea.focus(); + textArea.select(); + if (document.execCommand("copy")) { resolve(content); } else { reject(); } + textArea.remove(); } }); @@ -31,13 +34,3 @@ export async function copyToClipboard(content: string) { }); } } - -function selectElement(element: Element) { - const selection = window.getSelection(); - if (selection) { - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(element); - selection.addRange(range); - } -} diff --git a/frontend/src/screens/apps/AppCreated.tsx b/frontend/src/screens/apps/AppCreated.tsx index 44bd88eb..e03e494d 100644 --- a/frontend/src/screens/apps/AppCreated.tsx +++ b/frontend/src/screens/apps/AppCreated.tsx @@ -27,7 +27,6 @@ export default function AppCreated() { const queryParams = new URLSearchParams(search); const appId = queryParams.get("app") ?? ""; const appstoreApp = suggestedApps.find((app) => app.id === appId); - console.info(appstoreApp, appId); const [timeout, setTimeout] = useState(false); const [isQRCodeVisible, setIsQRCodeVisible] = useState(false); From f667675f8a4034c4d70c1093c4f7824016dcc6cd Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:40:25 +0700 Subject: [PATCH 39/47] fix: add LNServer_Wave as a trusted peer for no anchor channel reserves (#245) --- lnclient/ldk/ldk.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index f2e86020..eb1f6083 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -80,6 +80,7 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events lsp.OlympusLSP().Pubkey, lsp.AlbyPlebsLSP().Pubkey, lsp.MegalithLSP().Pubkey, + "02b4552a7a85274e4da01a7c71ca57407181752e8568b31d51f13c111a2941dce3", // LNServer_Wave "0296b2db342fcf87ea94d981757fdf4d3e545bd5cef4919f58b5d38dfdd73bf5c9", // blocktank // Mutinynet From 947f216fd29c40fddbdf938c6faa5ac3377d1de4 Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:54:46 +0700 Subject: [PATCH 40/47] fix: correctly pass TLVs in NIP-47 pay_keysend (#234) * fix: correctly pass TLVs in NIP-47 pay_keysend as per the nip-47 spec, pay_keysend TLV values are hex-encoded * Update lnclient/greenlight/greenlight.go Co-authored-by: Roland <33993199+rolznz@users.noreply.github.com> --------- Co-authored-by: Adithya Vardhan --- lnclient/breez/breez.go | 6 +++++- lnclient/greenlight/greenlight.go | 3 +-- lnclient/ldk/ldk.go | 6 +++++- lnclient/lnd/lnd.go | 6 +++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lnclient/breez/breez.go b/lnclient/breez/breez.go index 82bd1dd8..7b6245bd 100644 --- a/lnclient/breez/breez.go +++ b/lnclient/breez/breez.go @@ -119,9 +119,13 @@ func (bs *BreezService) SendPaymentSync(ctx context.Context, payReq string) (*ln func (bs *BreezService) SendKeysend(ctx context.Context, amount uint64, destination, preimage string, custom_records []lnclient.TLVRecord) (preImage string, err error) { extraTlvs := []breez_sdk.TlvEntry{} for _, record := range custom_records { + decodedValue, err := hex.DecodeString(record.Value) + if err != nil { + return "", err + } extraTlvs = append(extraTlvs, breez_sdk.TlvEntry{ FieldNumber: record.Type, - Value: []uint8(record.Value), + Value: decodedValue, }) } diff --git a/lnclient/greenlight/greenlight.go b/lnclient/greenlight/greenlight.go index 300b7e10..93f60fb7 100644 --- a/lnclient/greenlight/greenlight.go +++ b/lnclient/greenlight/greenlight.go @@ -2,7 +2,6 @@ package greenlight import ( "context" - "encoding/hex" "errors" "log" "math/rand" @@ -133,7 +132,7 @@ func (gs *GreenlightService) SendKeysend(ctx context.Context, amount uint64, des for _, customRecord := range custom_records { extraTlvs = append(extraTlvs, glalby.TlvEntry{ Ty: customRecord.Type, - Value: hex.EncodeToString([]byte(customRecord.Value)), + Value: customRecord.Value, // glalby expects hex-encoded TLV values }) } diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index eb1f6083..c37b1551 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -526,9 +526,13 @@ func (ls *LDKService) SendKeysend(ctx context.Context, amount uint64, destinatio customTlvs := []ldk_node.TlvEntry{} for _, customRecord := range custom_records { + decodedValue, err := hex.DecodeString(customRecord.Value) + if err != nil { + return "", err + } customTlvs = append(customTlvs, ldk_node.TlvEntry{ Type: customRecord.Type, - Value: []uint8(customRecord.Value), + Value: decodedValue, }) } diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index de9ca219..e307925e 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -359,7 +359,11 @@ func (svc *LNDService) SendKeysend(ctx context.Context, amount uint64, destinati destCustomRecords := map[uint64][]byte{} for _, record := range custom_records { - destCustomRecords[record.Type] = []byte(record.Value) + decodedValue, err := hex.DecodeString(record.Value) + if err != nil { + return "", err + } + destCustomRecords[record.Type] = decodedValue } const KEYSEND_CUSTOM_RECORD = 5482373484 destCustomRecords[KEYSEND_CUSTOM_RECORD] = preImageBytes From 3b8378ef78bad46b19d6f37afd2b5a290d78fd12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= <100827540+reneaaron@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:18:45 +0200 Subject: [PATCH 41/47] fix: remove bitcoin connect from apps (#243) --- .../assets/suggested-apps/bitcoin-connect.png | Bin 71152 -> 0 bytes frontend/src/components/SuggestedAppData.tsx | 8 -------- 2 files changed, 8 deletions(-) delete mode 100644 frontend/src/assets/suggested-apps/bitcoin-connect.png diff --git a/frontend/src/assets/suggested-apps/bitcoin-connect.png b/frontend/src/assets/suggested-apps/bitcoin-connect.png deleted file mode 100644 index 818595031b9dc8f61208da7d507554f4b93cb840..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71152 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 diff --git a/frontend/src/components/SuggestedAppData.tsx b/frontend/src/components/SuggestedAppData.tsx index 7fb22672..159c1c93 100644 --- a/frontend/src/components/SuggestedAppData.tsx +++ b/frontend/src/components/SuggestedAppData.tsx @@ -1,6 +1,5 @@ import alby from "src/assets/suggested-apps/alby.png"; import amethyst from "src/assets/suggested-apps/amethyst.png"; -import bc from "src/assets/suggested-apps/bitcoin-connect.png"; import damus from "src/assets/suggested-apps/damus.png"; import hablanews from "src/assets/suggested-apps/habla-news.png"; import kiwi from "src/assets/suggested-apps/kiwi.png"; @@ -126,13 +125,6 @@ export const suggestedApps: SuggestedApp[] = [ webLink: "https://lume.nu/", logo: lume, }, - { - id: "bitcoin-connect", - title: "Bitcoin Connect", - description: "Connect to apps", - webLink: "https://bitcoin-connect.com/", - logo: bc, - }, { id: "kiwi", title: "Kiwi", From 5a7b68a04b68f226caca013025a6f5e585692071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= <100827540+reneaaron@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:19:05 +0200 Subject: [PATCH 42/47] fix: validate theme from storage (#244) --- frontend/src/components/ui/theme-provider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ui/theme-provider.tsx b/frontend/src/components/ui/theme-provider.tsx index bb897bf9..d9be4651 100644 --- a/frontend/src/components/ui/theme-provider.tsx +++ b/frontend/src/components/ui/theme-provider.tsx @@ -35,7 +35,8 @@ export function ThemeProvider({ ...props }: ThemeProviderProps) { const [theme, setTheme] = useState(() => { - return (localStorage.getItem(storageKey) as Theme) || defaultTheme; + const themeFromStorage = localStorage.getItem(storageKey) as Theme; + return Themes.includes(themeFromStorage) ? themeFromStorage : defaultTheme; }); const [darkMode, setDarkMode] = useState(() => { From 340dcda2209962433cc4f4443246eb8fc047eab9 Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Thu, 11 Jul 2024 01:21:38 +0700 Subject: [PATCH 43/47] feat: improve open channel ux flows (#249) feat: improve open channel ui - re-add primary open channel button - add links to increase receiving capacity from outgoing and channel order pages --- frontend/src/screens/channels/Channels.tsx | 4 +- .../screens/channels/CurrentChannelOrder.tsx | 5 ++ .../channels/IncreaseOutgoingCapacity.tsx | 88 +++---------------- 3 files changed, 20 insertions(+), 77 deletions(-) diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 8f4c3f03..1356a7a7 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -363,9 +363,9 @@ export default function Channels() { - {/* + - */} + diff --git a/frontend/src/screens/channels/CurrentChannelOrder.tsx b/frontend/src/screens/channels/CurrentChannelOrder.tsx index 47149a5e..a18f5ce2 100644 --- a/frontend/src/screens/channels/CurrentChannelOrder.tsx +++ b/frontend/src/screens/channels/CurrentChannelOrder.tsx @@ -356,6 +356,11 @@ function PayBitcoinChannelOrderTopup({ order }: { order: NewChannelOrder }) { Topup with your credit card or bank account + + +
); diff --git a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx index 5e578b8e..242f478b 100644 --- a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx @@ -1,4 +1,4 @@ -import { Box, ChevronDown, Zap } from "lucide-react"; +import { ChevronDown } from "lucide-react"; import React, { FormEvent } from "react"; import { Link, useNavigate } from "react-router-dom"; import AppHeader from "src/components/AppHeader"; @@ -78,21 +78,13 @@ function NewChannelInternal({ network }: { network: Network }) { return _channelPeerSuggestions ? [ ..._channelPeerSuggestions.filter( - (peer) => - peer.paymentMethod !== "lightning" || peer.lspType !== "LSPS1" + (peer) => peer.paymentMethod !== "lightning" ), customOption, ] : undefined; }, [_channelPeerSuggestions, network]); - function setPaymentMethod(paymentMethod: "onchain" | "lightning") { - setOrder((current) => ({ - ...current, - paymentMethod, - })); - } - function setPublic(isPublic: boolean) { setOrder((current) => ({ ...current, @@ -131,21 +123,9 @@ function NewChannelInternal({ network }: { network: Network }) { host: selectedPeer.host, })); } - if ( - selectedPeer.paymentMethod === "lightning" && - order.paymentMethod === "lightning" - ) { - setOrder((current) => ({ - ...current, - lspType: selectedPeer.lspType, - lspUrl: selectedPeer.lspUrl, - })); - } } }, [order.paymentMethod, selectedPeer]); - const selectedCardStyles = "border-primary border-2 font-medium"; - const [showAdvanced, setShowAdvanced] = React.useState(false); function onSubmit(e: FormEvent) { @@ -219,6 +199,15 @@ function NewChannelInternal({ network }: { network: Network }) { + + + +
+ } /> {showAdvanced && ( <> -
- -
- setPaymentMethod("onchain")} - className="flex-1" - > -
- - Onchain -
- - setPaymentMethod("lightning")}> -
- - Lightning -
- -
-
{selectedPeer && - (selectedPeer.paymentMethod === "lightning" || - (order.paymentMethod === "onchain" && - selectedPeer.pubkey === order.pubkey)) && ( + order.paymentMethod === "onchain" && + selectedPeer.pubkey === order.pubkey && (
+ + + + + {validBudgetRenewals.map((renewalOption) => ( + + {renewalOption.charAt(0).toUpperCase() + renewalOption.slice(1)} + + ))} + + + ); +}; + +export default BudgetRenewalSelect; diff --git a/frontend/src/components/Permissions.tsx b/frontend/src/components/Permissions.tsx index d061ed62..2dbbf905 100644 --- a/frontend/src/components/Permissions.tsx +++ b/frontend/src/components/Permissions.tsx @@ -1,26 +1,19 @@ import { PlusCircle } from "lucide-react"; import React, { useEffect, useState } from "react"; +import BudgetAmountSelect from "src/components/BudgetAmountSelect"; +import BudgetRenewalSelect from "src/components/BudgetRenewalSelect"; import { Button } from "src/components/ui/button"; import { Checkbox } from "src/components/ui/checkbox"; import { Label } from "src/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "src/components/ui/select"; import { useCapabilities } from "src/hooks/useCapabilities"; import { cn } from "src/lib/utils"; import { AppPermissions, BudgetRenewalType, Scope, - budgetOptions, expiryOptions, iconMap, scopeDescriptions, - validBudgetRenewals, } from "src/types"; interface PermissionsProps { @@ -164,55 +157,17 @@ const Permissions: React.FC = ({ {!canEditPermissions ? ( permissions.budgetRenewal ) : ( - + /> )}
-
- {Object.keys(budgetOptions).map((budget) => { - return ( - // replace with something else and then remove dark prefixes -
- handleMaxAmountChange(budgetOptions[budget]) - } - className={`col-span-2 md:col-span-1 cursor-pointer rounded border-2 ${ - permissions.maxAmount == budgetOptions[budget] - ? "border-primary" - : "border-muted" - } text-center py-4 dark:text-white`} - > - {budget} -
- {budgetOptions[budget] ? "sats" : "#reckless"} -
- ); - })} -
+ ) : isNewConnection ? ( <> diff --git a/frontend/src/components/SidebarHint.tsx b/frontend/src/components/SidebarHint.tsx index 995d5d9d..3aa1a1c4 100644 --- a/frontend/src/components/SidebarHint.tsx +++ b/frontend/src/components/SidebarHint.tsx @@ -88,9 +88,9 @@ function SidebarHint() { return ( ); diff --git a/frontend/src/components/connections/AlbyConnectionCard.tsx b/frontend/src/components/connections/AlbyConnectionCard.tsx index 2c3639a8..da7302f2 100644 --- a/frontend/src/components/connections/AlbyConnectionCard.tsx +++ b/frontend/src/components/connections/AlbyConnectionCard.tsx @@ -6,7 +6,11 @@ import { Link2Icon, ZapIcon, } from "lucide-react"; +import { useState } from "react"; import { Link } from "react-router-dom"; + +import BudgetAmountSelect from "src/components/BudgetAmountSelect"; +import BudgetRenewalSelect from "src/components/BudgetRenewalSelect"; import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading"; import UserAvatar from "src/components/UserAvatar"; @@ -20,22 +24,36 @@ import { CardHeader, CardTitle, } from "src/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTrigger, +} from "src/components/ui/dialog"; +import { Label } from "src/components/ui/label"; import { LoadingButton } from "src/components/ui/loading-button"; import { Separator } from "src/components/ui/separator"; import { useAlbyMe } from "src/hooks/useAlbyMe"; import { LinkStatus, useLinkAccount } from "src/hooks/useLinkAccount"; -import { App } from "src/types"; +import { App, BudgetRenewalType } from "src/types"; +import linkAccountIllustration from "/images/illustrations/link-account.png"; function AlbyConnectionCard({ connection }: { connection?: App }) { const { data: albyMe } = useAlbyMe(); const { loading, linkStatus, loadingLinkStatus, linkAccount } = useLinkAccount(); + const [maxAmount, setMaxAmount] = useState(1_000_000); + const [budgetRenewal, setBudgetRenewal] = + useState("monthly"); + return ( - Alby Account + Linked Alby Account {connection && } @@ -47,7 +65,7 @@ function AlbyConnectionCard({ connection }: { connection?: App }) {
-
+
@@ -62,10 +80,47 @@ function AlbyConnectionCard({ connection }: { connection?: App }) {
{loadingLinkStatus && } {!connection || linkStatus === LinkStatus.SharedNode ? ( - - {!loading && } - Link your Alby Account - + + + + {!loading && } + Link your Alby Account + + + + Link to Alby Account + + After you link your account, your lightning address and + every app you access through your Alby Account will handle + payments via the Hub. + + You can add a budget that will restrict how much can be + spent from the Hub with your Alby Account. + +
+ + +
+ + + linkAccount(maxAmount, budgetRenewal)} + loading={loading} + > + Link to Alby Account + + +
+
) : linkStatus === LinkStatus.ThisNode ? ( +
+
+ {connection.lastEventAt && ( + <>Last used: {dayjs(connection.lastEventAt).fromNow()} + )} +
+ e.stopPropagation()} + > + +
)} diff --git a/frontend/src/hooks/useLinkAccount.ts b/frontend/src/hooks/useLinkAccount.ts index 9a858982..197704d7 100644 --- a/frontend/src/hooks/useLinkAccount.ts +++ b/frontend/src/hooks/useLinkAccount.ts @@ -4,6 +4,7 @@ import { useAlbyMe } from "src/hooks/useAlbyMe"; import { useApps } from "src/hooks/useApps"; import { useCSRF } from "src/hooks/useCSRF"; import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo"; +import { BudgetRenewalType } from "src/types"; import { request } from "src/utils/request"; export enum LinkStatus { @@ -32,7 +33,7 @@ export function useLinkAccount() { const loadingLinkStatus = linkStatus === undefined; - async function linkAccount() { + async function linkAccount(budget: number, renewal: BudgetRenewalType) { try { setLoading(true); if (!csrf) { @@ -44,6 +45,10 @@ export function useLinkAccount() { "X-CSRF-Token": csrf, "Content-Type": "application/json", }, + body: JSON.stringify({ + budget, + renewal, + }), }); // update the link status and get the newly-created Alby Account app await Promise.all([reloadAlbyMe(), reloadApps()]); diff --git a/frontend/src/screens/wallet/OnboardingChecklist.tsx b/frontend/src/screens/wallet/OnboardingChecklist.tsx index 689bb94c..af0deff4 100644 --- a/frontend/src/screens/wallet/OnboardingChecklist.tsx +++ b/frontend/src/screens/wallet/OnboardingChecklist.tsx @@ -88,7 +88,7 @@ function OnboardingChecklist() { to: "/wallet", }, { - title: "Link your Alby Account", + title: "Link to your Alby Account", description: "Link your lightning address & other apps to this Hub.", checked: isLinked, to: "/apps", diff --git a/frontend/src/types.ts b/frontend/src/types.ts index bc71726d..1009a91f 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -134,7 +134,7 @@ export interface App { scopes: Scope[]; maxAmount: number; budgetUsage: number; - budgetRenewal: string; + budgetRenewal: BudgetRenewalType; } export interface AppPermissions { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b70995dd..d0a22f51 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1563,9 +1563,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001565: - version "1.0.30001570" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz" - integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + version "1.0.30001640" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz" + integrity sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA== canvas-confetti@^1.9.2: version "1.9.2" diff --git a/http/alby_http_service.go b/http/alby_http_service.go index 22b93408..2493ea64 100644 --- a/http/alby_http_service.go +++ b/http/alby_http_service.go @@ -119,7 +119,14 @@ func (albyHttpSvc *AlbyHttpService) albyDrainHandler(c echo.Context) error { } func (albyHttpSvc *AlbyHttpService) albyLinkAccountHandler(c echo.Context) error { - err := albyHttpSvc.albyOAuthSvc.LinkAccount(c.Request().Context(), albyHttpSvc.svc.GetLNClient()) + var linkAccountRequest alby.AlbyLinkAccountRequest + if err := c.Bind(&linkAccountRequest); err != nil { + return c.JSON(http.StatusBadRequest, ErrorResponse{ + Message: fmt.Sprintf("Bad request: %s", err.Error()), + }) + } + + err := albyHttpSvc.albyOAuthSvc.LinkAccount(c.Request().Context(), albyHttpSvc.svc.GetLNClient(), linkAccountRequest.Budget, linkAccountRequest.Renewal) if err != nil { logger.Logger.WithError(err).Error("Failed to connect alby account") return err diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go index 2d47301a..d5e27969 100644 --- a/wails/wails_handlers.go +++ b/wails/wails_handlers.go @@ -505,7 +505,17 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string res := WailsRequestRouterResponse{Body: *infoResponse, Error: ""} return res case "/api/alby/link-account": - err := app.svc.GetAlbyOAuthSvc().LinkAccount(ctx, app.svc.GetLNClient()) + linkAccountRequest := &alby.AlbyLinkAccountRequest{} + err := json.Unmarshal([]byte(body), linkAccountRequest) + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "route": route, + "method": method, + "body": body, + }).WithError(err).Error("Failed to decode request to wails router") + return WailsRequestRouterResponse{Body: nil, Error: err.Error()} + } + err = app.svc.GetAlbyOAuthSvc().LinkAccount(ctx, app.svc.GetLNClient(), linkAccountRequest.Budget, linkAccountRequest.Renewal) if err != nil { return WailsRequestRouterResponse{Body: nil, Error: err.Error()} } From 1122f73a4c0e3b64bf7f1a23a049cdbee00a881f Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:33:43 +0700 Subject: [PATCH 47/47] fix: force dark mode in intro (#251) --- frontend/src/screens/Intro.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/frontend/src/screens/Intro.tsx b/frontend/src/screens/Intro.tsx index 12e8f00c..f70d9267 100644 --- a/frontend/src/screens/Intro.tsx +++ b/frontend/src/screens/Intro.tsx @@ -27,7 +27,16 @@ export function Intro() { const navigate = useNavigate(); const [api, setApi] = React.useState(); const [progress, setProgress] = React.useState(0); - const { theme } = useTheme(); + const { setDarkMode } = useTheme(); + + React.useEffect(() => { + // Force dark mode on intro screen + setDarkMode("dark"); + return () => { + // Revert to default after exiting intro + setDarkMode("system"); + }; + }, [setDarkMode]); React.useEffect(() => { if (!info?.setupCompleted) { @@ -49,7 +58,6 @@ export function Intro() { style={{ backgroundImage: `url(${Cloud})`, backgroundPositionX: `${-Math.max(progress, 0) * 40}%`, - filter: theme === "light" ? "invert(0.3)" : undefined, }} />