This commit is contained in:
Quan HL
2024-05-08 15:39:00 +07:00
parent 9b961c83f9
commit deb4c13531
6 changed files with 191 additions and 129 deletions

View File

@@ -1,5 +1,7 @@
import { import {
Application, Application,
ConferenceParticipantAction,
ConferenceParticipantActions,
FetchError, FetchError,
FetchTransport, FetchTransport,
Queue, Queue,
@@ -8,6 +10,7 @@ import {
} from "./types"; } from "./types";
import { MSG_SOMETHING_WRONG } from "./constants"; import { MSG_SOMETHING_WRONG } from "./constants";
import { getAdvancedSettings } from "src/storage"; import { getAdvancedSettings } from "src/storage";
import { EmptyData } from "src/common/types";
const fetchTransport = <Type>( const fetchTransport = <Type>(
url: string, url: string,
@@ -152,3 +155,21 @@ export const getSelfRegisteredUser = (username: string) => {
`${advancedSettings.apiServer}/Accounts/${advancedSettings.accountSid}/RegisteredSipUsers/${username}` `${advancedSettings.apiServer}/Accounts/${advancedSettings.accountSid}/RegisteredSipUsers/${username}`
); );
}; };
export const getConferences = () => {
const advancedSettings = getAdvancedSettings();
return getFetch<string[]>(
`${advancedSettings.apiServer}/Accounts/${advancedSettings.accountSid}/Conferences`
);
};
export const updateConferenceParticipantAction = (
callSid: string,
payload: ConferenceParticipantAction
) => {
const advancedSettings = getAdvancedSettings();
return putFetch<EmptyData, ConferenceParticipantAction>(
`${advancedSettings.apiServer}/Accounts/${advancedSettings.accountSid}/Calls/${callSid}`,
payload
);
};

View File

@@ -45,3 +45,20 @@ export interface RegisteredUser {
allow_direct_user_calling: boolean; allow_direct_user_calling: boolean;
registered_status: string; registered_status: string;
} }
export type ConferenceParticipantActions =
| "tag"
| "untag"
| "coach"
| "mute"
| "unmute"
| "hold"
| "unhold";
export interface ConferenceParticipantAction {
action: ConferenceParticipantActions;
tag: string;
}
export interface UpdateCall {
conferenceParticipantAction: ConferenceParticipantAction;
}

View File

@@ -62,12 +62,18 @@ export default class SipSession extends events.EventEmitter {
} }
}); });
this.#rtcSession.on("accepted", () => { this.#rtcSession.on(
"accepted",
({ response }: { response: IncomingResponse }) => {
this.emit(SipConstants.SESSION_ANSWERED, { this.emit(SipConstants.SESSION_ANSWERED, {
status: SipConstants.SESSION_ANSWERED, status: SipConstants.SESSION_ANSWERED,
callSid: response.hasHeader("X-Call-Sid")
? response.getHeader("X-Call-Sid")
: null,
}); });
this.#audio.playAnswer(undefined); this.#audio.playAnswer(undefined);
}); }
);
this.#rtcSession.on("failed", (data: EndEvent): void => { this.#rtcSession.on("failed", (data: EndEvent): void => {
let { originator, cause, message } = data; let { originator, cause, message } = data;
@@ -195,16 +201,19 @@ export default class SipSession extends events.EventEmitter {
direction: this.#rtcSession.direction, direction: this.#rtcSession.direction,
}); });
}); });
// pc.addEventListener('track', (event: RTCPeerConnectionEventMap["track"]): void => { pc.addEventListener(
// const stream: MediaStream = new MediaStream([event.track]) "track",
// if (this.#rtcSession.direction === 'outgoing') { (event: RTCPeerConnectionEventMap["track"]): void => {
// this.#audio.pauseRinging(); const stream: MediaStream = new MediaStream([event.track]);
// } if (this.#rtcSession.direction === "outgoing") {
// this.#audio.playRemote(stream, "track"); this.#audio.pauseRinging();
// this.emit(SipConstants.SESSION_TRACK, { }
// direction: this.#rtcSession.direction this.#audio.playRemote(stream);
// }); this.emit(SipConstants.SESSION_TRACK, {
// }); direction: this.#rtcSession.direction,
});
}
);
} }
get rtcSession() { get rtcSession() {

View File

@@ -1,10 +1,6 @@
import { import { SipSession, SipModel, SipConstants } from "./index";
SipSession, SipModel, SipConstants
} from "./index";
export default class SipSessionManager { export default class SipSessionManager {
#sessions: Map<string, SipModel.SipSessionState>; #sessions: Map<string, SipModel.SipSessionState>;
constructor() { constructor() {
@@ -24,7 +20,6 @@ export default class SipSessionManager {
} }
updateSession(field: string, session: SipSession, args: any): void { updateSession(field: string, session: SipSession, args: any): void {
const state: SipModel.SipSessionState = this.getSessionState(session.id); const state: SipModel.SipSessionState = this.getSessionState(session.id);
if (state) { if (state) {
switch (field) { switch (field) {
@@ -41,8 +36,8 @@ export default class SipSessionManager {
cause: args.cause, cause: args.cause,
status: args.status, status: args.status,
originator: args.endState, originator: args.endState,
description: args.description description: args.description,
} };
this.#sessions.delete(session.id); this.#sessions.delete(session.id);
break; break;
case SipConstants.SESSION_MUTED: case SipConstants.SESSION_MUTED:
@@ -51,8 +46,8 @@ export default class SipSessionManager {
case SipConstants.SESSION_HOLD: case SipConstants.SESSION_HOLD:
state.holdState = { state.holdState = {
originator: args.originator, originator: args.originator,
status: args.status status: args.status,
} };
break; break;
case SipConstants.SESSION_ICE_READY: case SipConstants.SESSION_ICE_READY:
state.iceReady = true; state.iceReady = true;
@@ -76,16 +71,13 @@ export default class SipSessionManager {
return this.getSessionState(id).sipSession; return this.getSessionState(id).sipSession;
} }
newSession(session: SipSession): void { newSession(session: SipSession): void {
this.#sessions.set(session.id, this.#sessions.set(session.id, {
{
id: session.id, id: session.id,
sipSession: session, sipSession: session,
startDateTime: new Date(), startDateTime: new Date(),
active: true, active: true,
status: 'init', status: "init",
}); });
} }

View File

@@ -28,8 +28,8 @@ export const IncommingCall = ({
as={FontAwesomeIcon} as={FontAwesomeIcon}
icon={faPhone} icon={faPhone}
color="jambonz.500" color="jambonz.500"
width="60px" width="30px"
height="60px" height="30px"
/> />
<Text fontSize="15px">Incoming call from</Text> <Text fontSize="15px">Incoming call from</Text>
<Text fontSize="24px" fontWeight="bold"> <Text fontSize="24px" fontWeight="bold">

View File

@@ -42,6 +42,7 @@ import { v4 as uuidv4 } from "uuid";
import IconButtonMenu, { IconButtonMenuItems } from "src/components/menu"; import IconButtonMenu, { IconButtonMenuItems } from "src/components/menu";
import { import {
getApplications, getApplications,
getConferences,
getQueues, getQueues,
getRegisteredUser, getRegisteredUser,
getSelfRegisteredUser, getSelfRegisteredUser,
@@ -126,6 +127,7 @@ export const Phone = ({
allow_direct_user_calling: false, allow_direct_user_calling: false,
} }
); );
const callSidRef = useRef("");
const toast = useToast(); const toast = useToast();
useEffect(() => { useEffect(() => {
@@ -204,9 +206,12 @@ export const Phone = ({
}, [status]); }, [status]);
useEffect(() => { useEffect(() => {
setInterval(() => { const timer = setInterval(() => {
fetchRegisterUser(); fetchRegisterUser();
}, 10_000); }, 10000);
return () => {
clearInterval(timer);
};
}, []); }, []);
const fetchRegisterUser = () => { const fetchRegisterUser = () => {
@@ -311,6 +316,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;
const currentCall = getCurrentCall(); const currentCall = getCurrentCall();
if (currentCall) { if (currentCall) {
currentCall.timeStamp = Date.now(); currentCall.timeStamp = Date.now();
@@ -628,17 +634,34 @@ 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.START_NEW_CONFERENCE); setPageView(PAGE_VIEW.START_NEW_CONFERENCE);
} else {
setPageView(PAGE_VIEW.JOIN_CONFERENCE);
}
}} }}
onOpen={() => { onOpen={() => {
return new Promise<IconButtonMenuItems[]>((resolve) => { return new Promise<IconButtonMenuItems[]>(
(resolve, reject) => {
getConferences()
.then(({ json }) => {
const sortedApps = json.sort((a, b) =>
a.localeCompare(b)
);
resolve([ resolve([
{ {
name: "Start New Conference", name: "Start new conference",
value: "start_new_conference", value: PAGE_VIEW.START_NEW_CONFERENCE.toString(),
}, },
...sortedApps.map((a) => ({
name: a,
value: a,
})),
]); ]);
}); })
.catch((err) => reject(err));
}
);
}} }}
/> />
</HStack> </HStack>