aboutsummaryrefslogtreecommitdiffstats
path: root/src/calls.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-18 09:27:09 -0700
committerAdam Soltys <[email protected]>2026-10-18 09:27:09 -0700
commitaaebbd5d51e0e149c36975122dd0ae59e6c011bb (patch)
treee3997c172c563fa0a29012ac18159d2be144d151 /src/calls.js
parentbfa52c7a2cf1cc06455536d9a6676b0003a30452 (diff)
downloadcoinosv3-aaebbd5d51e0e149c36975122dd0ae59e6c011bb.tar.gz
coinosv3-aaebbd5d51e0e149c36975122dd0ae59e6c011bb.zip
Call history: each call is a row in its conversation — Outgoing / Incoming video call · 4:12, Missed, Declined, No answer, Cancelled, Busy — a tap calls back. Kept like a message and sent to our own inbox (kind 1405, gift-wrapped to us alone) so our other devices list it; a call logged by two of our devices shows once, by its call-id. A call that rang while the app was shut and went unanswered is a missed call when it next opens.
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
Diffstat (limited to 'src/calls.js')
-rw-r--r--src/calls.js7
1 files changed, 6 insertions, 1 deletions
diff --git a/src/calls.js b/src/calls.js
index 98d0a46..827d605 100644
--- a/src/calls.js
+++ b/src/calls.js
@@ -66,6 +66,7 @@ export const candidateJson = (c) => JSON.stringify({ candidate: c.candidate, sdp
// media(type, facing) → MediaStream: microphone, and camera for 'video'
// mayRing(pk) whether this person may ring us (followed, or a conversation)
// changed() the call's state moved: draw it
+// ended(record) a call is over: { id, peer, type, dir, reason, connected, seconds, at } for its log
// trickle() false for a signer that's slow or asks each time (a
// bunker, Amber): the candidates go inside the offer or
// answer instead of one signed message each
@@ -93,6 +94,10 @@ export function createCalls(env) {
stopMedia(c);
c.state = 'ended'; c.reason = reason; c.endedAt = Date.now();
changed();
+ try {
+ if (env.ended) env.ended({ id: c.id, peer: c.peer, type: c.type, dir: c.dir, reason, at: c.offerAt || c.at, connected: !!c.connectedAt,
+ seconds: c.connectedAt ? Math.round((c.endedAt - c.connectedAt) / 1000) : 0 });
+ } catch {}
clearTimeout(endTimer);
endTimer = setTimeout(() => { if (call === c) { call = null; changed(); } }, ENDED_MS);
}
@@ -245,7 +250,7 @@ export function createCalls(env) {
if (!(await env.mayRing(from))) return;
clearTimeout(endTimer);
const type = tagOf(ev, 'call-type') === 'video' ? 'video' : 'voice';
- const n = call = { id, peer: from, type, dir: 'in', state: 'incoming', offer: ev.content, ice: [], at: Date.now(), muted: false, camOff: false, facing: 'user' };
+ const n = call = { id, peer: from, type, dir: 'in', state: 'incoming', offer: ev.content, ice: [], at: Date.now(), offerAt: ev.created_at * 1000, muted: false, camOff: false, facing: 'user' };
ringTimer = setTimeout(() => { if (call === n && n.state === 'incoming') end('missed', n); }, Math.max(5000, RING_MS - (Date.now() - ev.created_at * 1000)));
changed();
return;