Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
truncateDeliveredOps,
createTransfer,
getTransfer,
getPendingTransfers,
approveTransfer,
updateTransferStatus,
updateTransferResumeToken,
Expand Down Expand Up @@ -687,6 +688,35 @@ function handleMessage(client: Client, msg: Record<string, unknown>, ip: string)
}))
}
}

// Replay any still-pending transfer requests so an approver that was
// offline (or backgrounded — mobile PWAs drop their socket whenever the
// app loses focus) sees the approval prompt as soon as it (re)connects.
// Previously `transfer-requested` was a fire-and-forget `sendToDevice`
// that was silently lost if the approver had no live socket at that
// instant, and nothing ever re-delivered it — so the prompt never showed,
// "not even when they returned". An established device that joins is a
// candidate source/approver; a brand-new device (needsTransfer) has no
// notes to send, so it is never asked to approve.
if (!isNewDevice) {
const pending = getPendingTransfers(roomId)
for (const t of pending) {
if (t.status !== 'pending') continue // already approved/in-flight
if (t.requesterId === deviceId) continue // don't ask the requester to approve itself
// Only prompt when the requester is still connected to receive the data.
const requesterOnline = Array.from(room).some(
c => c.deviceId === t.requesterId && c.ws.readyState === WebSocket.OPEN,
)
if (!requesterOnline) continue
const requester = getDevice(roomId, t.requesterId)
client.ws.send(JSON.stringify({
type: 'transfer-requested',
transferId: t.id,
requesterId: t.requesterId,
requesterName: requester?.deviceName || 'A device',
}))
}
}
return
}

Expand Down
Loading