Skip to content

Commit

Permalink
Merge pull request ringcentral#54 from huanhulan/RCINT-5049
Browse files Browse the repository at this point in the history
Rcint 5049
  • Loading branch information
Lex. Huang authored Jun 11, 2018
2 parents e26358b + 7985368 commit 79dc47c
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import moduleActionTypes from '../../enums/moduleActionTypes';
export default new Enum([
...Object.keys(moduleActionTypes),
'mergeStart',
'mergeEnd',
'mergeSucceeded',
'mergeFailed',
// make conference call
'makeConference',
'makeConferenceSucceeded',
Expand All @@ -29,4 +30,7 @@ export default new Enum([
'removeFromConference',
'removeFromConferenceSucceeded',
'removeFromConferenceFailed',
// update merge pairs
'updateFromSession',
'updateToSession',
], 'conferenceCall');
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export function getMergingStatusReducer(types) {
switch (type) {
case types.mergeStart:
return true;
case types.mergeEnd:
case types.mergeSucceeded:
case types.mergeFailed:
case types.resetSuccess:
return false;
default:
Expand All @@ -88,11 +89,29 @@ export function getMergingStatusReducer(types) {
};
}

export function getMergingPairReduce(types) {
return (state = {}, { type, from, to }) => {
switch (type) {
case types.updateFromSession:
return { from };
case types.updateToSession:
return { ...state, to };
case types.mergeSucceeded:
case types.resetSuccess:
return {};
// restore the pair when failure
default:
return state;
}
};
}

export default function getConferenceCallReducer(types) {
return combineReducers({
status: getModuleStatusReducer(types),
conferences: getMakeConferenceCallReducer(types),
conferenceCallStatus: getConferenceCallStatusReducer(types),
isMerging: getMergingStatusReducer(types),
mergingPair: getMergingPairReduce(types),
});
}
94 changes: 63 additions & 31 deletions packages/ringcentral-integration/modules/ConferenceCall/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import permissionsMessages from '../RolesAndPermissions/permissionsMessages';
import conferenceErrors from './conferenceCallErrors';
// import webphoneErrors from '../Webphone/webphoneErrors';
import ensureExist from '../../lib/ensureExist';
import sleep from '../../lib/sleep';
import callingModes from '../CallingSettings/callingModes';

const DEFAULT_TTL = 5000;// timer to update the conference information
Expand Down Expand Up @@ -77,6 +78,7 @@ export default class ConferenceCall extends RcModule {
pulling = true,
capacity = MAXIMUM_CAPACITY,
spanForBringIn = DEFAULT_WAIT,
spanForTermination = DEFAULT_TERMINATION_SPAN,
...options
}) {
super({
Expand Down Expand Up @@ -109,6 +111,7 @@ export default class ConferenceCall extends RcModule {
this._timers = {};
this._pulling = pulling;
this._spanForBringIn = spanForBringIn;
this._spanForTermination = spanForTermination;
this.capacity = capacity;
}

Expand Down Expand Up @@ -177,6 +180,9 @@ export default class ConferenceCall extends RcModule {
if (this._webphone) {
if (conferenceData) {
this._webphone.hangup(conferenceData.session.id);
// Help server to do the GC, and we don't care the whether it's successful or not
this._client.service.platform()
.delete(`/account/~/telephony/sessions/${id}`);
this.store.dispatch({
type: this.actionTypes.terminateConferenceSucceeded,
conference: conferenceData.conference,
Expand Down Expand Up @@ -209,19 +215,19 @@ export default class ConferenceCall extends RcModule {
/**
* Bring-in an outbound call into conference.
* @param {string} id: conference id
* @param {call} partyCall: get it from callMonitor.\w+Calls[\d+]
* @param {webphone.session} webphoneSession: get it from callMonitor.\w+Calls[\d+]
* interface SessionData{
* "party-id": String,
* "session-id": String
* }
*/
@proxify
async bringInToConference(id, partyCall, propagete = false) {
async bringInToConference(id, webphoneSession, propagete = false) {
const conferenceState = this.state.conferences[id];
if (
!conferenceState
|| !partyCall
|| partyCall.direction !== callDirections.outbound
|| !webphoneSession
|| webphoneSession.direction !== callDirections.outbound
|| this.isOverload(id)
) {
if (!propagete) {
Expand All @@ -238,9 +244,9 @@ export default class ConferenceCall extends RcModule {
conference,
session,
});
const sessionData = partyCall.webphoneSession.data;
const sessionData = webphoneSession.data;
try {
const partyProfile = await this._getProfile(partyCall.webphoneSession);
const partyProfile = await this._getProfile(webphoneSession);
await this._client.service.platform()
.post(`/account/~/telephony/sessions/${id}/parties/bring-in`, sessionData);
await this.updateConferenceStatus(id);
Expand Down Expand Up @@ -340,11 +346,20 @@ export default class ConferenceCall extends RcModule {

/**
* Merge calls to (or create) a conference.
* @param {callMonitor.calls} calls
* @param {webphone.sessions} webphoneSessions
* FIXME: dynamically construct this function during the construction
* to avoid `this._webphone` criterias to improve performance ahead of time
*/
async mergeToConference(calls = []) {
async mergeToConference(webphoneSessions = []) {
webphoneSessions = webphoneSessions.filter(session => Object.prototype.toString.call(session).toLowerCase() === '[object object]');

if (!webphoneSessions.length) {
this._alert.warning({
message: conferenceErrors.bringInFailed,
});
return;
}

this.store.dispatch({
type: this.actionTypes.mergeStart,
});
Expand All @@ -358,7 +373,8 @@ export default class ConferenceCall extends RcModule {
* we cannot sure the merging process is over when
* the function's procedure has finshed.
*/
sipInstances = calls.map(c => this._webphone._sessions.get(c.webphoneSession.id));
sipInstances = webphoneSessions
.map(webphoneSession => this._webphone._sessions.get(webphoneSession.id));
const pSips = sipInstances.map((instance) => {
const p = new Promise((resolve) => {
instance.on('terminated', () => {
Expand All @@ -368,10 +384,10 @@ export default class ConferenceCall extends RcModule {
return p;
});

Promise.all([this._mergeToConference(calls), ...pSips])
await Promise.all([this._mergeToConference(webphoneSessions), ...pSips])
.then(() => {
this.store.dispatch({
type: this.actionTypes.mergeEnd,
type: this.actionTypes.mergeSucceeded,
});
}, () => {
const conferenceState = Object.values(this.conferences)[0];
Expand All @@ -386,15 +402,15 @@ export default class ConferenceCall extends RcModule {
message: conferenceErrors.bringInFailed,
});
this.store.dispatch({
type: this.actionTypes.mergeEnd,
type: this.actionTypes.mergeFailed,
});
});
} else {
try {
conferenceId = await this._mergeToConference(calls);
conferenceId = await this._mergeToConference(webphoneSessions);

this.store.dispatch({
type: this.actionTypes.mergeEnd,
type: this.actionTypes.mergeSucceeded,
});
} catch (e) {
const conferenceState = Object.values(this.conferences)[0];
Expand All @@ -411,18 +427,27 @@ export default class ConferenceCall extends RcModule {
}
if (!sipInstances || conferenceId === null) {
this.store.dispatch({
type: this.actionTypes.mergeEnd,
type: this.actionTypes.mergeFailed,
});
}
}
}

async _onStateChange() {
if (this._shouldInit()) {
this._init();
} else if (this._shouldReset()) {
this._reset();
/**
* we need to record the merge destination when merge from the call control pages
* @param {webphone.session} from
*/
setMergeParty({ from, to }) {
if (from) {
return this.store.dispatch({
type: this.actionTypes.updateFromSession,
from,
});
}
return this.store.dispatch({
type: this.actionTypes.updateToSession,
to,
});
}

getOnlinePartyProfiles(id) {
Expand Down Expand Up @@ -509,6 +534,14 @@ export default class ConferenceCall extends RcModule {
});
}

async _onStateChange() {
if (this._shouldInit()) {
this._init();
} else if (this._shouldReset()) {
this._reset();
}
}

_reset() {
this.store.dispatch({
type: this.actionTypes.resetSuccess
Expand Down Expand Up @@ -568,29 +601,28 @@ export default class ConferenceCall extends RcModule {
);
}

async _mergeToConference(calls = []) {
async _mergeToConference(webphoneSessions = []) {
const conferenceState = Object.values(this.conferences)[0];

if (conferenceState) {
const conferenceId = conferenceState.conference.id;
this.stopPollingConferenceStatus(conferenceId);
await Promise.all(
calls.map(
call => this.bringInToConference(conferenceId, call, true)
webphoneSessions.map(
webphoneSession => this.bringInToConference(conferenceId, webphoneSession, true)
)
);

/**
* HACK: terminate the session initiatively to avoid:
* 1. remaining session when duplicated session exsisting in a conference.
*/
setTimeout(() => {
calls.forEach((call) => {
if (call.webphoneSession && call.webphoneSession.id) {
this._webphone.hangup(call.webphoneSession.id);
}
});
}, DEFAULT_TERMINATION_SPAN);
await sleep(this._spanForTermination);
webphoneSessions.forEach((webphoneSession) => {
if (webphoneSession && webphoneSession.id) {
this._webphone.hangup(webphoneSession.id);
}
});

this.startPollingConferenceStatus(conferenceId);
return conferenceId;
Expand All @@ -602,7 +634,7 @@ export default class ConferenceCall extends RcModule {
* conference.
*/
await new Promise(resolve => setTimeout(resolve, this._spanForBringIn));
await this._mergeToConference(calls);
await this._mergeToConference(webphoneSessions);

return id;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ringcentral-integration/modules/Webphone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,8 @@ export default class Webphone extends RcModule {
) {
/**
* interface SessionData{
* "party-id": String,
* "session-id": String
* "partyId": String,
* "sessionId": String
* }
*/
session.data = incomingResponse.headers['P-Rc-Api-Ids'][0].raw.split(';')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export default function App({
)}
/>
<Route
path="/conferenceCall/dialer/:fromNumber"
path="/conferenceCall/dialer/"
component={routerProps => (
<ConferenceCallDialerPage
params={routerProps.params}
Expand Down
23 changes: 15 additions & 8 deletions packages/ringcentral-widgets/components/ActiveCallList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import classnames from 'classnames';
import ActiveCallItem from '../ActiveCallItem';
import styles from './styles.scss';

function isConferenceCall(normalizedCall) {
return normalizedCall.to.phoneNumber.length === 0 && normalizedCall.toName === 'Conference';
}

function ActiveCallList({
calls,
conference,
Expand All @@ -21,7 +25,7 @@ function ActiveCallList({
outboundSmsPermission,
internalSmsPermission,
isLoggedContact,
isConferenceCall,
isSessionAConferenceCall,
mergeToConference,
onLogCall,
autoLog,
Expand Down Expand Up @@ -55,10 +59,10 @@ function ActiveCallList({
const currentCall = activeCurrentCalls[0];
const hasConference = !!conference;
const isOnConferenceCall = call.webphoneSession
? isConferenceCall(call.webphoneSession.id)
: false;
? isSessionAConferenceCall(call.webphoneSession.id)
: isConferenceCall(call);// in case it's an other device call
const isCurrentCallAConf = currentCall
? isConferenceCall(currentCall.webphoneSession.id)
? isSessionAConferenceCall(currentCall.webphoneSession.id)
: false;

if (!isOnWebRTC) {
Expand All @@ -72,15 +76,18 @@ function ActiveCallList({
if (hasConference) {
showMergeCall = true;
if (isOnConferenceCall) {
onMergeCall = () => mergeToConference([currentCall]);
onMergeCall = () => mergeToConference([currentCall.webphoneSession]);
} else if (isCurrentCallAConf) {
onMergeCall = () => mergeToConference([call]);
onMergeCall = () => mergeToConference([call.webphoneSession]);
} else {
onMergeCall = () => onConfirmMergeCall(call);
}
} else {
showMergeCall = true;
const partyCalls = [call, activeCurrentCalls[0]];
const partyCalls = [
call.webphoneSession,
activeCurrentCalls[0].webphoneSession
];
onMergeCall = () => mergeToConference(partyCalls);
}
} else if (hasConference) {
Expand Down Expand Up @@ -155,7 +162,7 @@ ActiveCallList.propTypes = {
outboundSmsPermission: PropTypes.bool,
internalSmsPermission: PropTypes.bool,
isLoggedContact: PropTypes.func,
isConferenceCall: PropTypes.func.isRequired,
isSessionAConferenceCall: PropTypes.func.isRequired,
onLogCall: PropTypes.func,
loggingMap: PropTypes.object,
webphoneAnswer: PropTypes.func,
Expand Down
Loading

0 comments on commit 79dc47c

Please sign in to comment.