fix: sync-aware UI for Bitcoin-dependent apps

AppDetails.vue now checks Bitcoin sync progress for LND, ElectrumX,
BTCPay, and Mempool. Shows orange warning banner with sync progress
bar and block height when Bitcoin is still syncing. Users see clear
feedback instead of broken wallet connect pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-20 14:26:05 +00:00
parent 196682f2f2
commit b5024c29df

View File

@@ -229,6 +229,31 @@
<p class="text-white/60 text-sm mt-3 text-center">{{ t('appDetails.screenshotPlaceholder') }}</p>
</div>
<!-- Bitcoin Sync Warning (for dependent apps) -->
<div v-if="needsBitcoinSync && !bitcoinSynced" class="glass-card p-5 border border-orange-500/30">
<div class="flex items-center gap-3 mb-3">
<svg class="w-6 h-6 text-orange-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
<div class="flex-1">
<p class="text-orange-300 font-semibold text-sm">Bitcoin is syncing</p>
<p class="text-white/60 text-xs mt-0.5">
Some features may be unavailable until Bitcoin finishes syncing.
Wallet connections and block data require a fully synced node.
</p>
</div>
</div>
<div class="w-full h-2 bg-white/10 rounded-full overflow-hidden">
<div
class="h-full rounded-full bg-orange-400 transition-all duration-500"
:style="{ width: Math.min(bitcoinSyncPercent, 100) + '%' }"
></div>
</div>
<p class="text-xs text-white/40 mt-1.5">
{{ bitcoinSyncPercent.toFixed(1) }}% synced Block {{ bitcoinBlockHeight.toLocaleString() }}
</p>
</div>
<!-- Description -->
<div class="glass-card p-6">
<h2 class="text-2xl font-bold text-white mb-4">{{ t('appDetails.about', { name: pkg.manifest.title }) }}</h2>
@@ -472,7 +497,7 @@
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '../stores/app'
@@ -480,6 +505,7 @@ import { useAppLauncherStore } from '../stores/appLauncher'
import { PackageState } from '../types/api'
import { useModalKeyboard } from '@/composables/useModalKeyboard'
import { dummyApps } from '../utils/dummyApps'
import rpcClient from '@/api/rpc-client'
const router = useRouter()
const route = useRoute()
@@ -615,6 +641,32 @@ const gatewayState = computed(() => {
return gw ? gw.state : 'not installed'
})
/** Apps that depend on Bitcoin being synced */
const BITCOIN_DEPENDENT_APPS = ['lnd', 'electrumx', 'electrs', 'mempool-electrs', 'btcpay-server', 'btcpayserver']
const needsBitcoinSync = computed(() => BITCOIN_DEPENDENT_APPS.includes(packageKey.value))
const bitcoinSyncPercent = ref(0)
const bitcoinBlockHeight = ref(0)
const bitcoinSynced = computed(() => bitcoinSyncPercent.value >= 99.9)
async function loadBitcoinSync() {
if (!needsBitcoinSync.value) return
try {
const btc = await rpcClient.call<{ block_height: number; sync_progress: number }>({
method: 'bitcoin.getinfo',
timeout: 5000,
})
bitcoinSyncPercent.value = (btc.sync_progress ?? 0) * 100
bitcoinBlockHeight.value = btc.block_height ?? 0
} catch {
bitcoinSyncPercent.value = 0
bitcoinBlockHeight.value = 0
}
}
onMounted(() => {
loadBitcoinSync()
})
// Action error toast
const actionError = ref('')
let errorTimer: ReturnType<typeof setTimeout> | undefined