diff --git a/src/Components/Phone/Button/button.module.css b/src/Components/Phone/Button/button.module.css index 482aed7..2b976bc 100644 --- a/src/Components/Phone/Button/button.module.css +++ b/src/Components/Phone/Button/button.module.css @@ -25,14 +25,14 @@ left: 2px; } -.answer { +.answer, .makeCall { composes: button; background-color: #45bd57; } -.answer:hover { +.answer:hover, .makeCall:hover { background-image: linear-gradient(#84b68c, #5fb86c); } -.answer:after { +.answer:after, .makeCall:after{ content: url(/phone-solid.svg); transform: scale(0.65); } diff --git a/src/Components/Phone/dial-box.tsx b/src/Components/Phone/dial-box.tsx deleted file mode 100644 index 1b449c9..0000000 --- a/src/Components/Phone/dial-box.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -const DialBox = () => { - return; -} - -export default DialBox; \ No newline at end of file diff --git a/src/Components/Phone/display-device-state.tsx b/src/Components/Phone/display-device-state.tsx deleted file mode 100644 index 00ceda0..0000000 --- a/src/Components/Phone/display-device-state.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { DeviceState } from '../../hooks/voice-state'; - -export default function DisplayDeviceState({deviceState}: {deviceState: DeviceState}) -{ - return( -
- Device is currently {deviceState}. -
- ); -} \ No newline at end of file diff --git a/src/Components/Phone/phone.tsx b/src/Components/Phone/phone.tsx index 30adef6..b6b9751 100644 --- a/src/Components/Phone/phone.tsx +++ b/src/Components/Phone/phone.tsx @@ -2,47 +2,47 @@ import React from 'react'; import { Voice } from '../../wrappers/voice'; import { voiceToken } from '../../wrappers/default-config'; import Button from './Button/button'; -import DeviceState from './display-device-state'; -import DtmfButtons from './DtmfButtons/dtmf-buttons'; +import DeviceState from './DeviceState/display-device-state'; +import DialButtons from './DialButtons/dial-buttons'; +import DialBox from './DialBox/dial-box'; -export default function Phone({voice:{ startDevice, answer, reject, hangup, dial, dtmf, voiceState} }: {voice: Voice}) +export default function Phone({voice:{ startDevice, answer, reject, hangup, dialDigit, makeCall, voiceState} }: {voice: Voice}) { - if (voiceState.deviceState === "Offline") - { - return ( -
- -
- ); - } - else if(voiceState.deviceState === "Ringing") - { - return ( -
- -
- ); - } - else if(voiceState.deviceState === "Connected") - { - return ( -
- - -
- ); - } - else - { - return ( -
- -
- ); - } + switch (voiceState.deviceState) + { + case "Offline": + return ( +
+ +
+ ); + case "Ringing": + return ( +
+ +
+ ); + case "Connected": + return ( +
+ + + +
+ ); + default: + return ( +
+ + + +
+ ); + } } \ No newline at end of file diff --git a/src/Hooks/voice-state.ts b/src/Hooks/voice-state.ts index 4aa27de..406b038 100644 --- a/src/Hooks/voice-state.ts +++ b/src/Hooks/voice-state.ts @@ -8,11 +8,13 @@ export interface VoiceState callCreatedTime: number; callEstablishedTime: number; callPhoneNumber: string; + dialDigits: string; } export interface VoiceStateManager { onDeviceStateUpdate(state: DeviceState, phoneNumber?: string): void; + setDialDigits(digits: string): void; voiceState: VoiceState; } @@ -23,6 +25,9 @@ export default function useVoiceState(): VoiceStateManager const [callEstablishedTime, setCallEstablishedTime] = useState(0); const [callPhoneNumber, setCallPhoneNumber] = useState(""); + // Used for DTMF and number to dial + const [dialDigits, setDialDigits] = useState(""); + const onDeviceStateUpdate = (state: DeviceState, phoneNumber?: string) => { @@ -39,15 +44,20 @@ export default function useVoiceState(): VoiceStateManager setDeviceState(state); if (phoneNumber) { setCallPhoneNumber(phoneNumber); } + + // Wipe of the dialing digits on every state change. + setDialDigits(""); } return { onDeviceStateUpdate, + setDialDigits, voiceState: { deviceState, callCreatedTime, callEstablishedTime, - callPhoneNumber + callPhoneNumber, + dialDigits } } } \ No newline at end of file diff --git a/src/Mocks/mock-connection.ts b/src/Mocks/mock-connection.ts index 5cde274..e9bbe45 100644 --- a/src/Mocks/mock-connection.ts +++ b/src/Mocks/mock-connection.ts @@ -3,10 +3,10 @@ import { defaultDelay } from "./mock-device"; export class MockConnection implements Connection { - private from = "+16133713909"; - private to = "+123456789"; + private from: string = "+16133713909"; + private to: string = "+123456789"; - public parameters: ConnectionParameters = { From: this.from, To: this.to } as ConnectionParameters; + public get parameters() { return { From: this.from, To: this.to } as ConnectionParameters; } public customParameters: Map = new Map(); public options: object = {}; @@ -14,6 +14,16 @@ export class MockConnection implements Connection private readonly handlers = {} as {[key in ConnectionEvent]: ((mute?: boolean) => void)[]}; private muted: boolean = false; + constructor(phoneNumber?: string) + { + if (phoneNumber) + { + this.to = phoneNumber; + this.from = ""; + this.executeHandler("accept"); + } + } + private executeHandler(handlerName: ConnectionEvent) { setTimeout(() => this.handlers[handlerName]?.forEach(handler => handler()), defaultDelay); diff --git a/src/Mocks/mock-device.ts b/src/Mocks/mock-device.ts index fb06035..63f1502 100644 --- a/src/Mocks/mock-device.ts +++ b/src/Mocks/mock-device.ts @@ -8,23 +8,31 @@ export class MockDevice implements Device public audio: DeviceAudio = {} as DeviceAudio; private deviceStatus: DeviceStatus = "offline"; private readonly handlers = {} as {[key in DeviceEvent]: ((connection?: Connection) => void)[]}; + private currentConnection: Connection | undefined; + constructor(token: string, params?: SetupParams) { setTimeout(() => this.handlers["ready"].forEach(handler => handler()), defaultDelay); - setTimeout(() => this.handlers["incoming"].forEach(handler => handler(new MockConnection())), 2000); + setTimeout(() => + { + this.currentConnection = new MockConnection(); + this.handlers["incoming"].forEach(handler => handler(this.currentConnection)); + }, + 2000); } public setup(token: string, params?: SetupParams): void {} public connect(params: any, audioConstraints?: any): Connection { - return {} as Connection; + this.currentConnection = new MockConnection(params.To); + return this.currentConnection; } - public activeConnection(): Connection + public activeConnection(): Connection | undefined { - return {} as Connection; + return this.currentConnection; } public destroy(): void diff --git a/src/Wrappers/voice.ts b/src/Wrappers/voice.ts index 9c53447..86cd15a 100644 --- a/src/Wrappers/voice.ts +++ b/src/Wrappers/voice.ts @@ -9,8 +9,8 @@ export interface Voice answer(): void; reject(): void; hangup(): void; - dial(phoneNumber: string): void; - dtmf(digit: string): void; + dialDigit(digit: string): void; + makeCall(): void; voiceState: VoiceState; } @@ -72,20 +72,27 @@ export default function useVoice() device?.disconnectAll(); } - const dial = (phoneNumber: string) => + const dialDigit = (digit: string) => { - const connection = device?.connect({To: phoneNumber}); - - connection?.on("accept", () => state.onDeviceStateUpdate("Connected")); - connection?.on("disconnect", () => state.onDeviceStateUpdate("Disconnected")); - connection?.on("mute", muted => state.onDeviceStateUpdate(muted ? "Muted" : "Connected")); - - setConnection(connection); + if (state.voiceState.deviceState === "Connected") + { + connection?.sendDigits(digit); + } + state.setDialDigits(state.voiceState.dialDigits + digit); } - const dtmf = (digit: string) => + const makeCall = () => { - connection?.sendDigits(digit); + if (state.voiceState.dialDigits) + { + const connection = device?.connect({To: state.voiceState.dialDigits}); + + connection?.on("accept", () => state.onDeviceStateUpdate("Connected", connection.parameters.To)); + connection?.on("disconnect", () => state.onDeviceStateUpdate("Disconnected")); + connection?.on("mute", muted => state.onDeviceStateUpdate(muted ? "Muted" : "Connected")); + + setConnection(connection); + } } return { @@ -93,8 +100,8 @@ export default function useVoice() answer, reject, hangup, - dial, - dtmf, + dialDigit, + makeCall, voiceState: state.voiceState, } as Voice; } \ No newline at end of file diff --git a/src/components/Phone/DeviceState/display-device-state.module.css b/src/components/Phone/DeviceState/display-device-state.module.css new file mode 100644 index 0000000..434518d --- /dev/null +++ b/src/components/Phone/DeviceState/display-device-state.module.css @@ -0,0 +1,4 @@ +.phoneNumber { + font-size: 15px; + font-family: Arial, Helvetica, sans-serif; +} \ No newline at end of file diff --git a/src/components/Phone/DeviceState/display-device-state.tsx b/src/components/Phone/DeviceState/display-device-state.tsx new file mode 100644 index 0000000..3c9edc5 --- /dev/null +++ b/src/components/Phone/DeviceState/display-device-state.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { DeviceState } from '../../../hooks/voice-state'; +import styles from './display-device-state.module.css'; + +export default function DisplayDeviceState({deviceState, phoneNumber}: {deviceState: DeviceState, phoneNumber: string}) +{ + return( +
+ Device is currently {deviceState}. +

{phoneNumber}

+
+ ); +} \ No newline at end of file diff --git a/src/components/Phone/DialBox/dial-box.module.css b/src/components/Phone/DialBox/dial-box.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Phone/DialBox/dial-box.tsx b/src/components/Phone/DialBox/dial-box.tsx new file mode 100644 index 0000000..7f7f0b3 --- /dev/null +++ b/src/components/Phone/DialBox/dial-box.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import style from './dial-box.module.css'; + +export default function DialBox({dialDigits} : {dialDigits: string}) +{ + return
{dialDigits}
; +} \ No newline at end of file diff --git a/src/components/Phone/DtmfButtons/dtmf-buttons.module.css b/src/components/Phone/DialButtons/dial-buttons.module.css similarity index 87% rename from src/components/Phone/DtmfButtons/dtmf-buttons.module.css rename to src/components/Phone/DialButtons/dial-buttons.module.css index 09c2722..7427ed3 100644 --- a/src/components/Phone/DtmfButtons/dtmf-buttons.module.css +++ b/src/components/Phone/DialButtons/dial-buttons.module.css @@ -1,4 +1,4 @@ -.dtmf-button { +.dial-button { border-radius: 50%; border: 1px solid black; width: 70px; @@ -7,7 +7,7 @@ background-color: white; } -.dtmf-button:hover { +.dial-button:hover { background-color: lightgray; } diff --git a/src/components/Phone/DtmfButtons/dtmf-buttons.tsx b/src/components/Phone/DialButtons/dial-buttons.tsx similarity index 72% rename from src/components/Phone/DtmfButtons/dtmf-buttons.tsx rename to src/components/Phone/DialButtons/dial-buttons.tsx index bbad9f6..8d49f63 100644 --- a/src/components/Phone/DtmfButtons/dtmf-buttons.tsx +++ b/src/components/Phone/DialButtons/dial-buttons.tsx @@ -1,15 +1,14 @@ import React from 'react'; import { chunk } from 'lodash'; -import { Voice } from '../../../wrappers/voice'; -import styles from './dtmf-buttons.module.css'; +import styles from './dial-buttons.module.css'; -interface DtmfButton +interface DialButton { title: string; subTitle:string; } -const buttons: DtmfButton[] = [ +const buttons: DialButton[] = [ {title: "1", subTitle: " "}, {title: "2", subTitle: "abc"}, {title: "3", subTitle: "efg"}, @@ -24,13 +23,13 @@ const buttons: DtmfButton[] = [ {title: "#", subTitle: " "}, ]; -export default function DtmfButtons({dtmf}: {dtmf: Voice["dtmf"]}) { +export default function DialButtons({action}: {action: (digit: string) => void}) { return (
{ chunk(buttons, 3).map((buttonRow, rowIndex) =>
{ buttonRow.map(({title, subTitle}, index) => - ) } diff --git a/types/twilio-client.d.ts b/types/twilio-client.d.ts index 840e9dd..ce594aa 100644 --- a/types/twilio-client.d.ts +++ b/types/twilio-client.d.ts @@ -91,7 +91,7 @@ declare module "twilio-client" constructor(token: string, params?: SetupParams); public setup(token: string, params?: SetupParams): void; public connect(params: any, audioConstraints?: any): Connection; - public activeConnection(): Connection; + public activeConnection(): Connection | undefined; public destroy(): void; public disconnectAll(): void; public status(): DeviceStatus;