Skip to content

Commit 42cf870

Browse files
committed
WIP: feat: add "accept call?" prompt screen
1 parent f64b21e commit 42cf870

File tree

3 files changed

+71
-7
lines changed

3 files changed

+71
-7
lines changed

calls.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ window.webxdc.setUpdateListener(
5050
if (peer === selfAddr) {
5151
} else if (cmd === "start") {
5252
console.log("INCOMING CALL!");
53-
window.location.hash = "#acceptCall=" + payload;
53+
const noPrompt = false;
54+
window.location.hash =
55+
(noPrompt ? "#acceptCall=" : "#promptThenAcceptCall=") + payload;
5456
} else if (cmd === "accept") {
5557
console.log("CALL ACCEPTED!");
5658
window.location.hash = "#onAnswer=" + payload;

src/App.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import EndCallButton from "~/components/EndCallButton";
88
import AvatarPlaceholder from "~/components/AvatarPlaceholder";
99
import AvatarImage from "~/components/AvatarImage";
1010
import Button from "~/components/Button";
11+
import MaterialSymbolsCall from "~icons/material-symbols/call";
1112
import MaterialSymbolsVideocam from "~icons/material-symbols/videocam";
1213
import MaterialSymbolsVideocamOff from "~icons/material-symbols/videocam-off";
1314
import MaterialSymbolsMic from "~icons/material-symbols/mic";
@@ -123,11 +124,20 @@ export default function App() {
123124
manager.endCall();
124125
}, [manager]);
125126

126-
let status = "";
127-
if (state === "connecting") {
128-
status = "Connecting...";
129-
} else if (state === "ringing") {
130-
status = "Ringing...";
127+
let status: string;
128+
switch (state) {
129+
case "answerIncomingCallPrompt":
130+
status = "Incoming call"
131+
break
132+
case "connecting":
133+
status = "Connecting...";
134+
break;
135+
case "ringing":
136+
status = "Ringing...";
137+
break;
138+
case "in-call":
139+
status = "";
140+
break;
131141
}
132142

133143
const inCall = state === "in-call";
@@ -194,6 +204,21 @@ export default function App() {
194204
textAlign: "center",
195205
}}
196206
>
207+
{/* TODO transition */}
208+
{/* TODO narrow screens */}
209+
{state === "answerIncomingCallPrompt" && (
210+
<Button
211+
aria-label="Answer call"
212+
title="Answer call"
213+
onClick={() => manager.acceptCall()}
214+
style={{
215+
backgroundColor: "#00b000",
216+
...buttonsStyle,
217+
}}
218+
>
219+
<MaterialSymbolsCall />
220+
</Button>
221+
)}
197222
<Button
198223
aria-label={toggleAudioLabel}
199224
title={toggleAudioLabel}

src/lib/calls.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ const initialRtcConfiguration = {
3232
iceCandidatePoolSize: 1,
3333
} as RTCConfiguration;
3434

35-
export type CallState = "connecting" | "ringing" | "in-call";
35+
export type CallState =
36+
| "answerIncomingCallPrompt"
37+
| "connecting"
38+
| "ringing"
39+
| "in-call";
3640

3741
export class CallsManager {
3842
private peerConnection: RTCPeerConnection;
3943
private setIceServersPromise: Promise<void>;
4044
private state: CallState;
4145
static initialState = "connecting" as const;
4246

47+
resolveCallAcceptedPromise?: (accepted: boolean) => void;
48+
4349
private iceTricklingDataChannel: RTCDataChannel;
4450
/**
4551
* Stores local ICE candidates to be sent to the remote peer
@@ -142,6 +148,28 @@ export class CallsManager {
142148
if (hash === "startCall") {
143149
console.log("URL hash CMD: ", hash);
144150
await this.startCall();
151+
} else if (hash.startsWith("promptThenAcceptCall=")) {
152+
this.state = "answerIncomingCallPrompt";
153+
this.onStateChanged(this.state);
154+
155+
const offer = window.atob(hash.split("promptThenAcceptCall=", 2)[1]);
156+
console.log("URL hash CMD: promptThenAcceptCall:", offer);
157+
158+
const accepted = await new Promise<boolean>((r) => {
159+
this.resolveCallAcceptedPromise = r;
160+
});
161+
console.log(
162+
"Accept call prompt resolved, " +
163+
(accepted ? "accepted" : "rejected"),
164+
);
165+
if (!accepted) {
166+
this.endCall();
167+
} else {
168+
this.state = "connecting";
169+
this.onStateChanged(this.state);
170+
171+
await onIncomingCall(offer);
172+
}
145173
} else if (hash.startsWith("acceptCall=")) {
146174
const offer = window.atob(hash.substring(11));
147175
console.log("URL hash CMD: acceptCall:", offer);
@@ -178,9 +206,18 @@ export class CallsManager {
178206
this.onStateChanged(this.state);
179207
}
180208

209+
// TODO maybe just make this `undefined` if there is no need to accept it?
210+
acceptCall() {
211+
this.resolveCallAcceptedPromise?.(true);
212+
this.resolveCallAcceptedPromise = undefined;
213+
}
214+
181215
async endCall(): Promise<void> {
182216
this.peerConnection.close();
183217
window.calls.endCall();
218+
219+
this.resolveCallAcceptedPromise?.(false);
220+
this.resolveCallAcceptedPromise = undefined;
184221
}
185222

186223
getState() {

0 commit comments

Comments
 (0)