This commit is contained in:
Quan HL
2024-05-08 16:38:26 +07:00
parent deb4c13531
commit 7d6efbfbaf
4 changed files with 86 additions and 126 deletions

View File

@@ -201,19 +201,19 @@ export default class SipSession extends events.EventEmitter {
direction: this.#rtcSession.direction, direction: this.#rtcSession.direction,
}); });
}); });
pc.addEventListener( // pc.addEventListener(
"track", // "track",
(event: RTCPeerConnectionEventMap["track"]): void => { // (event: RTCPeerConnectionEventMap["track"]): void => {
const stream: MediaStream = new MediaStream([event.track]); // const stream: MediaStream = new MediaStream([event.track]);
if (this.#rtcSession.direction === "outgoing") { // if (this.#rtcSession.direction === "outgoing") {
this.#audio.pauseRinging(); // this.#audio.pauseRinging();
} // }
this.#audio.playRemote(stream); // this.#audio.playRemote(stream);
this.emit(SipConstants.SESSION_TRACK, { // this.emit(SipConstants.SESSION_TRACK, {
direction: this.#rtcSession.direction, // direction: this.#rtcSession.direction,
}); // });
} // }
); // );
} }
get rtcSession() { get rtcSession() {

View File

@@ -62,7 +62,6 @@ import {
faUserGroup, faUserGroup,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import NewConference from "./new-conference";
import JoinConference from "./join-conference"; import JoinConference from "./join-conference";
type PhoneProbs = { type PhoneProbs = {
@@ -80,7 +79,6 @@ enum PAGE_VIEW {
DIAL_PAD, DIAL_PAD,
INCOMING_CALL, INCOMING_CALL,
OUTGOING_CALL, OUTGOING_CALL,
START_NEW_CONFERENCE,
JOIN_CONFERENCE, JOIN_CONFERENCE,
} }
@@ -96,29 +94,16 @@ export const Phone = ({
}: PhoneProbs) => { }: PhoneProbs) => {
const [inputNumber, setInputNumber] = useState(""); const [inputNumber, setInputNumber] = useState("");
const [appName, setAppName] = useState(""); const [appName, setAppName] = useState("");
const inputNumberRef = useRef(inputNumber);
const [status, setStatus] = useState<SipClientStatus>("stop"); const [status, setStatus] = useState<SipClientStatus>("stop");
const [isConfigured, setIsConfigured] = useState(false); const [isConfigured, setIsConfigured] = useState(false);
const [callStatus, setCallStatus] = useState(SipConstants.SESSION_ENDED); const [callStatus, setCallStatus] = useState(SipConstants.SESSION_ENDED);
const [sessionDirection, setSessionDirection] = const [sessionDirection, setSessionDirection] =
useState<SipCallDirection>(""); useState<SipCallDirection>("");
const sessionDirectionRef = useRef(sessionDirection);
const sipUA = useRef<SipUA | null>(null);
const timerRef = useRef<NodeJS.Timer | null>(null);
const [seconds, setSeconds] = useState(0); const [seconds, setSeconds] = useState(0);
const secondsRef = useRef(seconds);
const [isCallButtonLoading, setIsCallButtonLoading] = useState(false); const [isCallButtonLoading, setIsCallButtonLoading] = useState(false);
const [isAdvanceMode, setIsAdvancedMode] = useState(false); const [isAdvanceMode, setIsAdvancedMode] = useState(false);
const isRestartRef = useRef(false);
const sipDomainRef = useRef("");
const sipUsernameRef = useRef("");
const sipPasswordRef = useRef("");
const sipServerAddressRef = useRef("");
const sipDisplayNameRef = useRef("");
const [isSwitchingUserStatus, setIsSwitchingUserStatus] = useState(true); const [isSwitchingUserStatus, setIsSwitchingUserStatus] = useState(true);
const [isOnline, setIsOnline] = useState(false); const [isOnline, setIsOnline] = useState(false);
const unregisteredReasonRef = useRef("");
const isInputNumberFocusRef = useRef(false);
const [pageView, setPageView] = useState<PAGE_VIEW>(PAGE_VIEW.DIAL_PAD); const [pageView, setPageView] = useState<PAGE_VIEW>(PAGE_VIEW.DIAL_PAD);
const [registeredUser, setRegisteredUser] = useState<Partial<RegisteredUser>>( const [registeredUser, setRegisteredUser] = useState<Partial<RegisteredUser>>(
{ {
@@ -127,7 +112,23 @@ export const Phone = ({
allow_direct_user_calling: false, allow_direct_user_calling: false,
} }
); );
const callSidRef = useRef(""); const [selectedConference, setSelectedConference] = useState("");
const [callSid, setCallSid] = useState("");
const inputNumberRef = useRef(inputNumber);
const sessionDirectionRef = useRef(sessionDirection);
const sipUA = useRef<SipUA | null>(null);
const timerRef = useRef<NodeJS.Timer | null>(null);
const isRestartRef = useRef(false);
const sipDomainRef = useRef("");
const sipUsernameRef = useRef("");
const sipPasswordRef = useRef("");
const sipServerAddressRef = useRef("");
const sipDisplayNameRef = useRef("");
const unregisteredReasonRef = useRef("");
const isInputNumberFocusRef = useRef(false);
const secondsRef = useRef(seconds);
const toast = useToast(); const toast = useToast();
useEffect(() => { useEffect(() => {
@@ -170,14 +171,26 @@ export const Phone = ({
if (isSipClientIdle(callStatus) && isCallButtonLoading) { if (isSipClientIdle(callStatus) && isCallButtonLoading) {
setIsCallButtonLoading(false); setIsCallButtonLoading(false);
} }
if (isSipClientRinging(callStatus)) { switch (callStatus) {
if (sessionDirection === "incoming") { case SipConstants.SESSION_RINGING:
setPageView(PAGE_VIEW.INCOMING_CALL); if (sessionDirection === "incoming") {
} else { setPageView(PAGE_VIEW.INCOMING_CALL);
setPageView(PAGE_VIEW.OUTGOING_CALL); } else {
} setPageView(PAGE_VIEW.OUTGOING_CALL);
} else { }
setPageView(PAGE_VIEW.DIAL_PAD); break;
case SipConstants.SESSION_ANSWERED:
if (!!selectedConference) {
setPageView(PAGE_VIEW.JOIN_CONFERENCE);
} else {
setPageView(PAGE_VIEW.DIAL_PAD);
}
break;
case SipConstants.SESSION_ENDED:
case SipConstants.SESSION_FAILED:
setSelectedConference("");
setPageView(PAGE_VIEW.DIAL_PAD);
break;
} }
}, [callStatus]); }, [callStatus]);
@@ -316,7 +329,7 @@ export const Phone = ({
setInputNumber(args.session.user); setInputNumber(args.session.user);
}); });
sipClient.on(SipConstants.SESSION_ANSWERED, (args) => { sipClient.on(SipConstants.SESSION_ANSWERED, (args) => {
callSidRef.current = args.callSid; setCallSid(args.callSid);
const currentCall = getCurrentCall(); const currentCall = getCurrentCall();
if (currentCall) { if (currentCall) {
currentCall.timeStamp = Date.now(); currentCall.timeStamp = Date.now();
@@ -634,11 +647,10 @@ export const Phone = ({
tooltip="Join a conference" tooltip="Join a conference"
noResultLabel="No conference" noResultLabel="No conference"
onClick={(name, value) => { onClick={(name, value) => {
if (value === PAGE_VIEW.START_NEW_CONFERENCE.toString()) { setPageView(PAGE_VIEW.JOIN_CONFERENCE);
setPageView(PAGE_VIEW.START_NEW_CONFERENCE); setSelectedConference(
} else { value === PAGE_VIEW.JOIN_CONFERENCE.toString() ? "" : value
setPageView(PAGE_VIEW.JOIN_CONFERENCE); );
}
}} }}
onOpen={() => { onOpen={() => {
return new Promise<IconButtonMenuItems[]>( return new Promise<IconButtonMenuItems[]>(
@@ -651,7 +663,7 @@ export const Phone = ({
resolve([ resolve([
{ {
name: "Start new conference", name: "Start new conference",
value: PAGE_VIEW.START_NEW_CONFERENCE.toString(), value: PAGE_VIEW.JOIN_CONFERENCE.toString(),
}, },
...sortedApps.map((a) => ({ ...sortedApps.map((a) => ({
name: a, name: a,
@@ -779,15 +791,14 @@ export const Phone = ({
)} )}
{pageView === PAGE_VIEW.JOIN_CONFERENCE && ( {pageView === PAGE_VIEW.JOIN_CONFERENCE && (
<JoinConference <JoinConference
conferenceId={selectedConference}
callSid={callSid}
handleCancel={() => { handleCancel={() => {
setPageView(PAGE_VIEW.DIAL_PAD); setPageView(PAGE_VIEW.DIAL_PAD);
}} }}
/> call={(name) => {
)} setSelectedConference(name);
{pageView === PAGE_VIEW.START_NEW_CONFERENCE && ( sipUA.current?.call(`conf:${name}`);
<NewConference
handleCancel={() => {
setPageView(PAGE_VIEW.DIAL_PAD);
}} }}
/> />
)} )}

View File

@@ -9,23 +9,41 @@ import {
Text, Text,
VStack, VStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { FormEvent } from "react"; import { FormEvent, useState } from "react";
import OutlineBox from "src/components/outline-box"; import OutlineBox from "src/components/outline-box";
type JoinConferenceProbs = { type JoinConferenceProbs = {
conferenceId?: string;
callSid: string;
handleCancel: () => void; handleCancel: () => void;
call: (conference: string) => void;
}; };
export const JoinConference = ({ handleCancel }: JoinConferenceProbs) => { export const JoinConference = ({
conferenceId,
handleCancel,
call,
}: JoinConferenceProbs) => {
const [conferenceName, setConferenceName] = useState(conferenceId || "");
const handleSubmit = (e: FormEvent) => { const handleSubmit = (e: FormEvent) => {
e.preventDefault(); e.preventDefault();
call(conferenceName);
}; };
return ( return (
<Box as="form" onSubmit={handleSubmit} w="full"> <Box as="form" onSubmit={handleSubmit} w="full">
<VStack spacing={4} mt="20px" w="full"> <VStack spacing={4} mt="20px" w="full">
<Text fontWeight="bold">Joining conference</Text> <Text fontWeight="bold">
{!!conferenceId ? "Joining" : "Start"} conference
</Text>
<FormControl id="conference_name"> <FormControl id="conference_name">
<FormLabel>Conference name</FormLabel> <FormLabel>Conference name</FormLabel>
<Input type="text" placeholder="Name" isRequired /> <Input
type="text"
placeholder="Name"
isRequired
value={conferenceName}
onChange={(e) => setConferenceName(e.target.value)}
disabled={!!conferenceId}
/>
</FormControl> </FormControl>
<OutlineBox title="Join as"> <OutlineBox title="Join as">
@@ -35,17 +53,17 @@ export const JoinConference = ({ handleCancel }: JoinConferenceProbs) => {
<FormControl id="speak_only_to"> <FormControl id="speak_only_to">
<FormLabel>Speak only to</FormLabel> <FormLabel>Speak only to</FormLabel>
<Input type="text" placeholder="agent" isRequired /> <Input type="text" placeholder="agent" />
</FormControl> </FormControl>
<FormControl id="tag"> <FormControl id="tag">
<FormLabel>Tag</FormLabel> <FormLabel>Tag</FormLabel>
<Input type="text" placeholder="tags" isRequired /> <Input type="text" placeholder="tags" />
</FormControl> </FormControl>
</OutlineBox> </OutlineBox>
<HStack w="full"> <HStack w="full">
<Button colorScheme="jambonz" type="submit" w="full"> <Button colorScheme="jambonz" type="submit" w="full">
Start Conference {!!conferenceId ? "Join conference" : "Start conference"}
</Button> </Button>
<Button <Button

View File

@@ -1,69 +0,0 @@
import {
Box,
Button,
Checkbox,
FormControl,
FormLabel,
HStack,
Input,
Text,
VStack,
} from "@chakra-ui/react";
import { FormEvent } from "react";
import OutlineBox from "src/components/outline-box";
type NewConferenceProbs = {
handleCancel: () => void;
};
export const NewConference = ({ handleCancel }: NewConferenceProbs) => {
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
};
return (
<Box as="form" onSubmit={handleSubmit} w="full">
<VStack spacing={4} mt="20px" w="full">
<Text fontWeight="bold" fontSize="2xl">
Start new conference
</Text>
<FormControl id="conference_name">
<FormLabel>Conference name</FormLabel>
<Input type="text" placeholder="Name" isRequired />
</FormControl>
<OutlineBox title="Join as">
<Checkbox colorScheme="jambonz">Full participant</Checkbox>
<Checkbox colorScheme="jambonz">Muted</Checkbox>
<Checkbox colorScheme="jambonz">Coach mode</Checkbox>
<FormControl id="speak_only_to">
<FormLabel>Speak only to</FormLabel>
<Input type="text" placeholder="agent" isRequired />
</FormControl>
<FormControl id="tag">
<FormLabel>Tag</FormLabel>
<Input type="text" placeholder="tags" isRequired />
</FormControl>
</OutlineBox>
<HStack w="full">
<Button colorScheme="jambonz" type="submit" w="full">
Start Conference
</Button>
<Button
colorScheme="grey"
type="button"
w="full"
textColor="black"
onClick={handleCancel}
>
Cancel
</Button>
</HStack>
</VStack>
</Box>
);
};
export default NewConference;