2026-08-02 07:36:25 -04:00
|
|
|
// Shared "this action needs a working Lightning node" gate (2026-08-02).
|
2026-08-02 07:23:37 -04:00
|
|
|
//
|
2026-08-02 07:36:25 -04:00
|
|
|
// Creating a Lightning invoice used to fail at the RPC layer whenever this
|
|
|
|
|
// node had no usable Lightning implementation — `lnd.createinvoice` returned
|
|
|
|
|
// connection-refused and the Receive screen rendered "Operation failed. Check
|
|
|
|
|
// server logs for details." That reads as the wallet being broken, when the
|
|
|
|
|
// truth is a missing (or stopped) prerequisite the user can act on.
|
2026-08-02 07:23:37 -04:00
|
|
|
//
|
2026-08-02 07:36:25 -04:00
|
|
|
// Callers ask `requireLightningNode()` BEFORE attempting the call. When there
|
|
|
|
|
// is no usable node it opens the global LightningRequiredModal and returns
|
|
|
|
|
// false, so the caller bails without surfacing an error at all.
|
2026-08-02 07:23:37 -04:00
|
|
|
//
|
2026-08-02 07:36:25 -04:00
|
|
|
// Keyed on package STATE, not mere presence: `package-data` carries an entry
|
|
|
|
|
// for a Lightning app that is known to this node but not actually running, so
|
|
|
|
|
// `id in packages` is NOT "installed and usable" — that assumption was the
|
|
|
|
|
// first version's bug, and it let the raw RPC error through on a node with no
|
|
|
|
|
// lnd container at all.
|
2026-08-02 07:23:37 -04:00
|
|
|
import { ref } from 'vue'
|
|
|
|
|
import { useAppStore } from '@/stores/app'
|
2026-08-02 07:36:25 -04:00
|
|
|
import { PackageState } from '@/types/api'
|
2026-08-02 07:23:37 -04:00
|
|
|
|
|
|
|
|
/** Package ids that provide a Lightning node.
|
|
|
|
|
*
|
|
|
|
|
* `lnd` ships today. Core Lightning is the next implementation the modal
|
|
|
|
|
* offers — when its app id lands in the catalog, add it here and flip its
|
2026-08-02 07:36:25 -04:00
|
|
|
* `available` flag in LightningRequiredModal; nothing else changes. */
|
2026-08-02 07:23:37 -04:00
|
|
|
export const LIGHTNING_NODE_APP_IDS = ['lnd'] as const
|
|
|
|
|
|
2026-08-02 07:36:25 -04:00
|
|
|
/** `absent` — nothing installed, offer to install one.
|
|
|
|
|
* `stopped` — installed but not running, point the user at My Apps.
|
|
|
|
|
* `running` — good to go. */
|
|
|
|
|
export type LightningStatus = 'absent' | 'stopped' | 'running'
|
|
|
|
|
|
2026-08-02 07:23:37 -04:00
|
|
|
// Module-scope: one source of truth shared by every caller and the single
|
|
|
|
|
// global modal mounted in App.vue.
|
|
|
|
|
const show = ref(false)
|
2026-08-02 07:36:25 -04:00
|
|
|
const status = ref<LightningStatus>('absent')
|
2026-08-02 07:23:37 -04:00
|
|
|
|
|
|
|
|
export function useLightningRequired() {
|
|
|
|
|
const appStore = useAppStore()
|
|
|
|
|
|
2026-08-02 07:36:25 -04:00
|
|
|
/** Best status across every known Lightning implementation. */
|
|
|
|
|
function lightningStatus(): LightningStatus {
|
|
|
|
|
const pkgs = (appStore.packages ?? {}) as Record<string, { state?: string } | undefined>
|
|
|
|
|
let best: LightningStatus = 'absent'
|
|
|
|
|
for (const id of LIGHTNING_NODE_APP_IDS) {
|
|
|
|
|
const entry = pkgs[id]
|
|
|
|
|
if (!entry) continue
|
|
|
|
|
if (entry.state === PackageState.Running) return 'running'
|
|
|
|
|
// Present but not running: installing, starting, stopped, exited…
|
|
|
|
|
best = 'stopped'
|
|
|
|
|
}
|
|
|
|
|
return best
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-02 07:23:37 -04:00
|
|
|
function hasLightningNode(): boolean {
|
2026-08-02 07:36:25 -04:00
|
|
|
return lightningStatus() === 'running'
|
2026-08-02 07:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gate a Lightning-only action. Returns true to proceed; returns false and
|
2026-08-02 07:36:25 -04:00
|
|
|
* opens the modal (in the mode matching why) when there is no usable node.
|
2026-08-02 07:23:37 -04:00
|
|
|
*/
|
|
|
|
|
function requireLightningNode(): boolean {
|
2026-08-02 07:36:25 -04:00
|
|
|
const s = lightningStatus()
|
|
|
|
|
if (s === 'running') return true
|
|
|
|
|
status.value = s
|
2026-08-02 07:23:37 -04:00
|
|
|
show.value = true
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function close() {
|
|
|
|
|
show.value = false
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-02 07:36:25 -04:00
|
|
|
return { show, status, lightningStatus, hasLightningNode, requireLightningNode, close }
|
2026-08-02 07:23:37 -04:00
|
|
|
}
|