fix(lnd): make the seed-backup notification actually open the backup flow

Clicking 'Back up now' on the Lightning seed banner only navigated to
the LND app page — and the backup card there hid itself permanently if
its single status fetch failed (common right after a fresh install),
so the click appeared to do nothing.

The banner now deep-links with ?seed-backup=1, which the LND page uses
to open the reveal flow directly, and the card retries its status fetch
with backoff instead of giving up after one failed RPC.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-16 10:06:03 -04:00
parent 72d7fa07ff
commit 47dea8cd55
2 changed files with 27 additions and 4 deletions

View File

@ -101,7 +101,10 @@ const show = computed(() =>
)
function backUpNow() {
router.push(LND_DETAILS_PATH).catch(() => {})
// seed-backup=1 makes the LND page open the reveal flow immediately
// landing on the app page with the card below the fold read as
// "clicking the notification did nothing".
router.push({ path: LND_DETAILS_PATH, query: { 'seed-backup': '1' } }).catch(() => {})
}
function snooze() {

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { rpcClient } from '@/api/rpc-client'
// Lightning seed (aezeed) backup card. Shown on the LND app detail page.
@ -7,11 +8,14 @@ import { rpcClient } from '@/api/rpc-client'
// confirms writing it down (`acknowledged`), the card renders as a
// prominent first-launch prompt.
const route = useRoute()
const router = useRouter()
const available = ref(false)
const acknowledged = ref(true)
const statusLoaded = ref(false)
async function loadStatus() {
async function loadStatus(): Promise<boolean> {
try {
const res = await rpcClient.call<{ available: boolean; acknowledged: boolean }>({
method: 'lnd.seed-backup-status',
@ -20,12 +24,28 @@ async function loadStatus() {
available.value = !!res.available
acknowledged.value = !!res.acknowledged
statusLoaded.value = true
return true
} catch {
statusLoaded.value = false
// Leave statusLoaded as-is; a one-off RPC blip must not permanently
// hide the card (the global banner may have just promised it's here).
return false
}
}
onMounted(loadStatus)
onMounted(async () => {
// Retry a few times on fresh installs the backend can still be warming
// up when the user lands here straight from the backup banner.
for (let attempt = 0; attempt < 4; attempt++) {
if (await loadStatus()) break
await new Promise((r) => setTimeout(r, 1500 * (attempt + 1)))
}
// Deep link from the "Back up your Lightning seed" banner: open the
// reveal flow directly so the click visibly does something.
if (route.query['seed-backup'] === '1') {
router.replace({ query: { ...route.query, 'seed-backup': undefined } }).catch(() => {})
if (statusLoaded.value && available.value) openReveal()
}
})
// Reveal modal re-auth gated (password + 2FA when enabled), same UX as
// the recovery-phrase reveal in Settings Backup.