From 0c7cf4fcf61903c230f9997331dd46d74a414ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= <100827540+reneaaron@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:11:12 +0100 Subject: [PATCH] fix: delete lightning address when deleting a sub-wallet (#1858) * fix: delete lightning address when deleting a sub-wallet * fix: pass app to useDeleteApp hook * fix: move deletion to server * fix: merge changes * Update frontend/src/components/connections/DisconnectApp.tsx Co-authored-by: Adithya Vardhan * fix: create helper function --------- Co-authored-by: Roland Bewick Co-authored-by: Adithya Vardhan --- api/api.go | 10 +++++ apps/apps_service.go | 16 +++++++ .../components/connections/DisconnectApp.tsx | 18 +++++--- frontend/src/hooks/useDeleteApp.ts | 45 ++++++++++--------- frontend/src/screens/apps/AppsCleanup.tsx | 35 +++++++++++---- 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/api/api.go b/api/api.go index 771e8593..a97e15ba 100644 --- a/api/api.go +++ b/api/api.go @@ -320,6 +320,16 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e } func (api *api) DeleteApp(userApp *db.App) error { + // Delete lightning address if one exists + if api.appsSvc.HasLightningAddress(userApp) { + err := api.DeleteLightningAddress(context.Background(), userApp.ID) + if err != nil { + logger.Logger.WithError(err).WithFields(logrus.Fields{ + "app_id": userApp.ID, + }).Error("Failed to delete lightning address during app deletion") + } + } + return api.appsSvc.DeleteApp(userApp) } diff --git a/apps/apps_service.go b/apps/apps_service.go index 5d532246..c29988d8 100644 --- a/apps/apps_service.go +++ b/apps/apps_service.go @@ -26,6 +26,7 @@ type AppsService interface { GetAppByPubkey(pubkey string) *db.App GetAppById(id uint) *db.App SetAppMetadata(appId uint, metadata map[string]interface{}) error + HasLightningAddress(app *db.App) bool } type appsService struct { @@ -232,3 +233,18 @@ func (svc *appsService) SetAppMetadata(id uint, metadata map[string]interface{}) return nil } + +func (svc *appsService) HasLightningAddress(app *db.App) bool { + if app.Metadata == nil { + return false + } + + var metadata map[string]interface{} + err := json.Unmarshal(app.Metadata, &metadata) + if err != nil { + return false + } + + lud16, exists := metadata["lud16"] + return exists && lud16 != nil +} diff --git a/frontend/src/components/connections/DisconnectApp.tsx b/frontend/src/components/connections/DisconnectApp.tsx index 3122d78f..3ebc7d89 100644 --- a/frontend/src/components/connections/DisconnectApp.tsx +++ b/frontend/src/components/connections/DisconnectApp.tsx @@ -25,7 +25,7 @@ export function DisconnectApp({ }) { const navigate = useNavigate(); - const { deleteApp, isDeleting } = useDeleteApp(() => { + const { deleteApp, isDeleting } = useDeleteApp(app, () => { navigate( app.metadata?.app_store_app_id !== SUBWALLET_APPSTORE_APP_ID ? "/apps?tab=connected-apps" @@ -33,6 +33,11 @@ export function DisconnectApp({ ); }); + // Check if this is a sub-wallet with a lightning address + const isSubwallet = + app.metadata?.app_store_app_id === SUBWALLET_APPSTORE_APP_ID; + const hasLightningAddress = !!app.metadata?.lud16; + return ( @@ -54,14 +59,17 @@ export function DisconnectApp({ remain in your wallet. )} + {isSubwallet && hasLightningAddress && ( +

+ This sub-wallet has a lightning address ({app.metadata?.lud16}) + that will also be deleted. +

+ )} Cancel - deleteApp(app.appPubkey)} - disabled={isDeleting} - > + Confirm diff --git a/frontend/src/hooks/useDeleteApp.ts b/frontend/src/hooks/useDeleteApp.ts index 83228d9d..6cf70948 100644 --- a/frontend/src/hooks/useDeleteApp.ts +++ b/frontend/src/hooks/useDeleteApp.ts @@ -1,34 +1,35 @@ import React from "react"; import { toast } from "sonner"; +import { App } from "src/types"; import { handleRequestError } from "src/utils/handleRequestError"; import { request } from "src/utils/request"; -export function useDeleteApp(onSuccess?: (appPubkey: string) => void) { +export function useDeleteApp(app: App, onSuccess?: () => void) { const [isDeleting, setDeleting] = React.useState(false); - const deleteApp = React.useCallback( - async (appPubkey: string) => { - setDeleting(true); - try { - await request(`/api/apps/${appPubkey}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - }); - toast("Connection deleted"); - if (onSuccess) { - onSuccess(appPubkey); - } - } catch (error) { - await handleRequestError("Failed to delete connection", error); - } finally { - setDeleting(false); + const deleteApp = React.useCallback(async () => { + setDeleting(true); + try { + // Delete the app/sub-wallet + await request(`/api/apps/${app.appPubkey}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + + toast("Connection deleted"); + + if (onSuccess) { + onSuccess(); } - }, - [onSuccess] - ); + } catch (error) { + await handleRequestError("Failed to delete connection", error); + } finally { + setDeleting(false); + } + }, [onSuccess, app]); return React.useMemo( () => ({ deleteApp, isDeleting }), diff --git a/frontend/src/screens/apps/AppsCleanup.tsx b/frontend/src/screens/apps/AppsCleanup.tsx index a0cacc13..df48e3cc 100644 --- a/frontend/src/screens/apps/AppsCleanup.tsx +++ b/frontend/src/screens/apps/AppsCleanup.tsx @@ -24,7 +24,6 @@ export function AppsCleanup() { const [skippedCount, setSkippedCount] = React.useState(0); const [deletedCount, setDeletedCount] = React.useState(0); const [appsToReview, setAppsToReview] = React.useState(); - const { deleteApp } = useDeleteApp(); React.useEffect(() => { if (!unusedApps) { return; @@ -86,17 +85,13 @@ export function AppsCleanup() { Skip - + /> } readonly @@ -133,3 +128,25 @@ export function AppsCleanup() { ); } + +function DeleteAppButton({ + app, + onDelete, +}: { + app: App; + onDelete: () => void; +}) { + const { deleteApp } = useDeleteApp(app); + return ( + + ); +}