feat(ui): peer requests confirm via modal with optional message

Request-to-Peer (Federation Discover modal + Web5 Node Visibility list)
no longer fires instantly: a BaseModal confirm shows the target, an
optional 280-char message (handshake.connect already carried it), and
Send/Cancel with the standard orange CTA.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-14 09:28:21 -04:00
co-authored by Claude Fable 5
parent 9471d3727f
commit e113a2560e
3 changed files with 114 additions and 10 deletions
+21 -3
View File
@@ -87,7 +87,7 @@
<button
class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs text-white/90 hover:text-white disabled:opacity-50 shrink-0"
:disabled="requestingPeer === node.nostr_pubkey || requestedPeers.has(node.nostr_pubkey)"
@click="requestToPeer(node)"
@click="requestModalTarget = node"
>
{{ requestedPeers.has(node.nostr_pubkey) ? 'Requested' : requestingPeer === node.nostr_pubkey ? 'Sending…' : 'Request to Peer' }}
</button>
@@ -99,6 +99,14 @@
<p v-if="discoverEnabled" class="mt-3 text-xs text-amber-400/80">
{{ t('web5.discoverableWarning') }}
</p>
<PeerRequestModal
:show="requestModalTarget !== null"
:target-label="requestModalTarget ? shortNpub(requestModalTarget.nostr_npub) : ''"
:sending="requestingPeer !== null"
@send="confirmPeerRequest"
@cancel="requestModalTarget = null"
/>
</div>
</template>
@@ -107,6 +115,7 @@ import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { rpcClient } from '@/api/rpc-client'
import ToggleSwitch from '@/components/ToggleSwitch.vue'
import PeerRequestModal from '@/components/federation/PeerRequestModal.vue'
import { safeClipboardWrite } from './utils'
import type { VisibilityLevel } from './types'
@@ -203,13 +212,22 @@ async function discoverNodes() {
}
}
async function requestToPeer(node: DiscoverableNode) {
const requestModalTarget = ref<DiscoverableNode | null>(null)
async function confirmPeerRequest(message: string | undefined) {
const node = requestModalTarget.value
if (!node) return
await requestToPeer(node, message)
requestModalTarget.value = null
}
async function requestToPeer(node: DiscoverableNode, message?: string) {
if (requestingPeer.value) return
requestingPeer.value = node.nostr_pubkey
try {
// Connection requests always land as Peer (observer) on approval —
// never trusted — so a mistaken request can't hand over fleet access.
await rpcClient.handshakeConnect(node.nostr_pubkey)
await rpcClient.handshakeConnect(node.nostr_pubkey, message)
requestedPeers.value.add(node.nostr_pubkey)
requestedPeers.value = new Set(requestedPeers.value)
emit('toast', 'Peer request sent — awaiting their approval')