fix: connect AI funding scan to the wallet scanner

This commit is contained in:
archipelago
2026-10-08 12:47:40 -04:00
parent 82367dc1d5
commit 0ff95251f5
3 changed files with 26 additions and 3 deletions
+12
View File
@@ -385,3 +385,15 @@ change the allowance, send a payment, start inference, or publish content. API
keys use the existing private node credential store and are not passed to AIUI.
Focused provider/publishing tests and production qualification are in progress;
these changes are not yet deployed or publicly released.
Provider-focused validation: 20 dashboard/setup/signer tests, 22 AIUI provider
and generation tests, and 22 trusted bridge/integration tests pass. Initial
publishing tests required an AI-connection component stub for their isolated
mounts; the provider default test now checks initial state before the suite's
explicit Claude selection. The full backend suite and production builds remain
pending. Framework still runs the preceding candidate; its management service is
active and no Ollama container exists after cleanup.
The funding modal's Scan action now opens the existing wallet scanner and returns
to funding on close; six focused connection-modal tests pass after that wiring.
The first dashboard production build passed; it will be rebuilt for this final
scanner wiring before deployment. AIUI and isolated backend builds are ongoing.
@@ -28,7 +28,8 @@
<button v-if="status?.local_ready" class="glass-button rounded-lg px-4 py-2 text-sm text-white mt-4 w-full" @click="useLocal">Use local AI</button>
<p class="text-xs text-white/40 mt-4">Your draft stays in place when you close this window.</p>
</BaseModal>
<ReceiveBitcoinModal :show="show && funding" initial-method="ecash" @close="finishFunding" @received="finishFunding" />
<ReceiveBitcoinModal :show="show && funding && !scanning" initial-method="ecash" @close="finishFunding" @received="finishFunding" @scan="scanning = true" />
<WalletScanModal :show="show && scanning" @close="finishScan" @sent="finishScan" />
</template>
<script setup lang="ts">
@@ -36,6 +37,7 @@ import { computed, ref, watch } from 'vue'
import { rpcClient } from '@/api/rpc-client'
import BaseModal from './BaseModal.vue'
import ReceiveBitcoinModal from './ReceiveBitcoinModal.vue'
import WalletScanModal from './WalletScanModal.vue'
import RoutstrBudgetSection from '@/views/settings/RoutstrBudgetSection.vue'
type Choice = 'claude' | 'openai' | 'routstr'
@@ -59,6 +61,7 @@ const error = ref('')
const loading = ref(false)
const saving = ref(false)
const funding = ref(false)
const scanning = ref(false)
const configured = computed(() => mode.value === 'claude' ? status.value?.claude_configured : status.value?.openai_configured)
let refreshPromise: Promise<ProviderStatus | null> | null = null
async function refresh(): Promise<ProviderStatus | null> {
@@ -96,7 +99,7 @@ async function refreshBalance() {
catch { balance.value = null }
}
function choose(choice: Choice) { key.value = ''; error.value = ''; mode.value = choice; if (choice === 'routstr') void refreshBalance() }
function close() { key.value = ''; funding.value = false; emit('close') }
function close() { key.value = ''; funding.value = false; scanning.value = false; emit('close') }
async function save() {
if ((mode.value !== 'claude' && mode.value !== 'openai') || saving.value) return
const provider = mode.value
@@ -128,8 +131,9 @@ async function continueRoutstr() {
emit('configured', 'routstr'); close()
} catch { error.value = 'Could not select Routstr. Try again.' }
}
async function finishScan() { scanning.value = false; await refreshBalance() }
async function finishFunding() { funding.value = false; await refreshBalance() }
watch(() => props.show, open => { if (open) { error.value = ''; void refresh(); if (mode.value === 'routstr') void refreshBalance() } else { key.value = ''; funding.value = false } })
watch(() => props.show, open => { if (open) { error.value = ''; void refresh(); if (mode.value === 'routstr') void refreshBalance() } else { key.value = ''; funding.value = false; scanning.value = false } })
async function syncSelection() { const state = await refresh(); if (state) emit('configured', state.settings.provider, state.settings.provider === 'openai' ? state.settings.openai_model : undefined) }
defineExpose({ checkNeeded, syncSelection, showRoutstr: () => choose('routstr') })
</script>
@@ -2,8 +2,10 @@ import { mount, flushPromises } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import AIConnectionModal from '../AIConnectionModal.vue'
import ReceiveBitcoinModal from '../ReceiveBitcoinModal.vue'
import WalletScanModal from '../WalletScanModal.vue'
import { rpcClient } from '@/api/rpc-client'
vi.mock('@/api/rpc-client', () => ({ rpcClient: { call: vi.fn() } }))
vi.mock('../WalletScanModal.vue', () => ({ default: { props: ['show'], template: '<div />' } }))
vi.mock('../ReceiveBitcoinModal.vue', () => ({ default: { props: ['show', 'initialMethod'], template: '<div />' } }))
vi.mock('@/views/settings/RoutstrBudgetSection.vue', () => ({ default: { template: '<div />' } }))
const state = () => ({ schema: 1, settings: { provider: 'auto', openai_model: '' }, claude_configured: false, openai_configured: false, local_ready: false, routstr_remaining_sats: 0 })
@@ -18,6 +20,11 @@ describe('AI connection setup', () => {
expect(vi.mocked(rpcClient.call).mock.calls.every(([r]) => !['assistant.budget-set', 'system.settings.set'].includes(r.method))).toBe(true)
await button(w, 'Top up with ecash').trigger('click')
expect(w.findComponent(ReceiveBitcoinModal).props()).toMatchObject({ show: true, initialMethod: 'ecash' })
w.findComponent(ReceiveBitcoinModal).vm.$emit('scan'); await flushPromises()
expect(w.findComponent(WalletScanModal).props('show')).toBe(true)
expect(w.findComponent(ReceiveBitcoinModal).props('show')).toBe(false)
w.findComponent(WalletScanModal).vm.$emit('close'); await flushPromises()
expect(w.findComponent(ReceiveBitcoinModal).props('show')).toBe(true)
w.unmount()
})