demo · v140

Action Router

The accept/reject buttons on a call notification are inline actions. Each click resolves a different intent — accept, decline, send-to-voicemail, "text me instead" — and the SW dispatches it back to your VoIP backend. The router laid out as a real component, with the platform call.

L
Lawrence Loft

incoming call · Workspace

click an action to see the route

// In a SW: handle the action distinct from the click body.
self.addEventListener("notificationclick", e => {
  if (e.action === "accept") {
    e.waitUntil(clients.openWindow(`/call/${e.notification.data.id}/accept`));
  } else if (e.action === "reject") {
    e.waitUntil(fetch(`/api/call/${e.notification.data.id}/reject`, { method: "POST" }));
  } else if (e.action === "text") {
    e.waitUntil(clients.openWindow(`/inbox/sms/${e.notification.data.peerId}?text=Can%27t+talk`));
  }
});

// Show the call notification:
self.registration.showNotification("Lawrence Loft", {
  body: "incoming call",
  scenario: "incoming-call",         // new in 140 — system treats as ring
  vibrate: [400, 200, 400, 200, 400],
  actions: [
    { action: "accept", title: "Accept" },
    { action: "reject", title: "Decline" },
    { action: "text",   title: "Text me" },
  ],
  data: { id: "abc123", peerId: "u-lloft" },
});

see also