feat(federation): cancel button for outbound pending peer requests
Previously the Pending Peer Requests panel only had Approve/Reject for
inbound rows; outbound rows in the 'sent' state had no action and
would sit there until the target explicitly approved or rejected. Now
you can Cancel an outbound request — the local row is dropped and a
PeerCancel nostr DM is sent so the target's inbound row also
disappears.
Backend:
- HandshakeMessage::PeerCancel {reason: Option<String>} variant.
- nostr_handshake::send_peer_cancel() mirrors send_peer_reject.
- handshake.poll handler dispatches inbound PeerCancel: finds the
matching inbound pending row (same from_nostr_pubkey, state=Pending)
and deletes it. Reply shape gains `cancelled_inbound: [id]`.
- federation::pending::delete() — hard-remove (set_state only
transitions; we don't want 'Cancelled' ghosts in the audit trail).
- federation.cancel-request RPC: outbound+Sent only, default
notify=true (cancelling silently is a footgun), best-effort DM
(relay failure doesn't block local deletion). Wired in dispatcher.
Frontend:
- PendingRequestsPanel.vue: Cancel button appears only on
outbound+sent rows. Emits 'cancel' event with request id.
- Federation.vue: cancelPending(id) handler calls
rpcClient.federationCancelRequest and reloads the list.
- rpcClient.federationCancelRequest(id, reason?, notify=true).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a658e924e1
commit
95f52572fc
@@ -742,6 +742,25 @@ class RPCClient {
|
||||
})
|
||||
}
|
||||
|
||||
/// Withdraw an outbound peer request we sent. Default notify=true so
|
||||
/// the recipient's inbound row disappears from their UI (the main
|
||||
/// UX reason to cancel — avoid leaving a stale handshake dangling).
|
||||
async federationCancelRequest(
|
||||
id: string,
|
||||
reason?: string,
|
||||
notify = true,
|
||||
): Promise<{ cancelled: boolean; id: string; notified: boolean }> {
|
||||
return this.call({
|
||||
method: 'federation.cancel-request',
|
||||
params: {
|
||||
id,
|
||||
...(reason ? { reason } : {}),
|
||||
notify,
|
||||
},
|
||||
timeout: 30000,
|
||||
})
|
||||
}
|
||||
|
||||
async federationSyncState(): Promise<{
|
||||
synced: number
|
||||
failed: number
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
@poll="pollHandshake"
|
||||
@approve="approvePending"
|
||||
@reject="rejectPending"
|
||||
@cancel="cancelPending"
|
||||
/>
|
||||
|
||||
<NodeList
|
||||
@@ -346,6 +347,22 @@ async function rejectPending(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelPending(id: string) {
|
||||
pendingBusyId.value = id
|
||||
discoveryError.value = ''
|
||||
try {
|
||||
// Default notify=true from the rpc-client — the peer's inbound row
|
||||
// disappears from their UI so they don't have to wonder about a
|
||||
// stale handshake.
|
||||
await rpcClient.federationCancelRequest(id)
|
||||
await loadPendingRequests()
|
||||
} catch (e: unknown) {
|
||||
discoveryError.value = e instanceof Error ? e.message : 'Cancel failed'
|
||||
} finally {
|
||||
pendingBusyId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function isOnlineCheck(node: FederatedNode): boolean {
|
||||
if (!node.last_seen) return false
|
||||
const lastSeen = new Date(node.last_seen).getTime()
|
||||
|
||||
@@ -56,6 +56,16 @@
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="req.outbound && req.state === 'sent'" class="flex flex-col gap-2 shrink-0">
|
||||
<button
|
||||
class="px-3 py-1 glass-button glass-button-sm rounded text-xs text-white/70 hover:text-red-300 disabled:opacity-50"
|
||||
:disabled="busyId === req.id"
|
||||
:title="'Withdraw the request and notify the peer to drop their pending row'"
|
||||
@click="$emit('cancel', req.id)"
|
||||
>
|
||||
{{ busyId === req.id ? '…' : 'Cancel' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,6 +86,7 @@ defineEmits<{
|
||||
poll: []
|
||||
approve: [id: string]
|
||||
reject: [id: string]
|
||||
cancel: [id: string]
|
||||
}>()
|
||||
|
||||
// Hide already-handled rows older than 24h to keep the panel from growing
|
||||
|
||||
Reference in New Issue
Block a user