feat(prejoin_page): Add ability for guest to join audio by phone
This commit is contained in:
parent
2ddfead4f5
commit
b2895b7095
|
@ -0,0 +1,75 @@
|
|||
.cpick {
|
||||
border: 1px solid #A4B8D1;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 15px;
|
||||
height: 38px;
|
||||
line-height: 24px;
|
||||
|
||||
&-selector {
|
||||
align-items: center;
|
||||
background-color: #283447;
|
||||
border-right: 1px solid #A4B8D1;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
position: relative;
|
||||
width: 88px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 8px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 12px;
|
||||
|
||||
& > svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
padding: 8px;
|
||||
background: #1C2025;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
caret-color: #0376DA;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
height: 190px;
|
||||
overflow-y: auto;
|
||||
width: 343px;
|
||||
}
|
||||
|
||||
&-dropdown-entry {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: #66768b;
|
||||
}
|
||||
|
||||
&-text {
|
||||
color: #fff;
|
||||
flex-grow: 1;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override @Atlaskit/inline-dialog styles
|
||||
.cpick-container > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
.prejoin-dialog {
|
||||
background: #1C2025;
|
||||
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
height: 400px;
|
||||
width: 375px;
|
||||
|
||||
&--small {
|
||||
height: 300;
|
||||
width: 400;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
|
||||
&-num {
|
||||
background: #2b3b4b;
|
||||
border: 1px solid #A4B8D1;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
align-items: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&-flag {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
cursor: pointer;
|
||||
|
||||
> svg {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
&-btn {
|
||||
width: 309px;
|
||||
}
|
||||
|
||||
&-dialin-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-delimiter {
|
||||
background: #5f6266;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
&-container {
|
||||
margin: 16px 0 24px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-txt-container {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: -8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-txt {
|
||||
background: #1C2025;
|
||||
color: #5f6266;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-callout {
|
||||
padding: 16px;
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-picker {
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-dialin {
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
align-items: center;
|
||||
margin: 16px 0 32px 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&-num {
|
||||
background: #3e474f;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
|
||||
&-container {
|
||||
min-height: 48px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
color: #6FB1EA;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-spaced-label {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
&-btns {
|
||||
&> div {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-calling {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
|
||||
&-number {
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
|
@ -91,5 +91,7 @@ $flagsImagePath: "../images/";
|
|||
@import 'audio-preview';
|
||||
@import 'video-preview';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'country-picker';
|
||||
|
||||
/* Modules END */
|
||||
|
|
|
@ -489,6 +489,12 @@
|
|||
"dialInPin": "Dial into the meeting and enter PIN code:",
|
||||
"dialing": "Dialing",
|
||||
"doNotShow": "Don't show this again",
|
||||
"errorDialOut": "Could not dial out",
|
||||
"errorDialOutDisconnected": "Could not dial out. Disconnected",
|
||||
"errorDialOutFailed": "Could not dial out. Call failed",
|
||||
"errorDialOutStatus": "Error getting dial out status",
|
||||
"errorStatusCode": "Error dialing out, status code: {{status}}",
|
||||
"errorValidation": "Number validation failed",
|
||||
"iWantToDialIn": "I want to dial in",
|
||||
"joinAudioByPhone": "Join with phone audio",
|
||||
"joinMeeting": "Join meeting",
|
||||
|
|
|
@ -10,3 +10,23 @@ export * from './functions.any';
|
|||
*/
|
||||
export function _cleanupConfig(config: Object) { // eslint-disable-line no-unused-vars
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dial out url.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDialOutStatusUrl(state: Object): string {
|
||||
return state['features/base/config'].guestDialOutStatusUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dial out status url.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDialOutUrl(state: Object): string {
|
||||
return state['features/base/config'].guestDialOutUrl;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@
|
|||
* Opens URL in the browser.
|
||||
*
|
||||
* @param {string} url - The URL to be opened.
|
||||
* @param {boolean} openInNewTab - If the link should be opened in a new tab.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function openURLInBrowser(url: string) {
|
||||
window.open(url, '', 'noopener');
|
||||
export function openURLInBrowser(url: string, openInNewTab?: boolean) {
|
||||
const target = openInNewTab ? '_blank' : '';
|
||||
|
||||
window.open(url, target, 'noopener');
|
||||
}
|
||||
|
|
|
@ -165,9 +165,10 @@ export function updateDialInNumbers() {
|
|||
const state = getState();
|
||||
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
|
||||
= state['features/base/config'];
|
||||
const { numbersFetched } = state['features/invite'];
|
||||
const mucURL = hosts && hosts.muc;
|
||||
|
||||
if (!dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
|
||||
if (numbersFetched || !dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
|
||||
// URLs for fetching dial in numbers not defined
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { i18next } from '../base/i18n';
|
||||
import { isLocalParticipantModerator } from '../base/participants';
|
||||
import { toState } from '../base/redux';
|
||||
import { doGetJSON, parseURIString } from '../base/util';
|
||||
|
||||
import logger from './logger';
|
||||
|
@ -616,3 +617,69 @@ export function _decodeRoomURI(url: string) {
|
|||
|
||||
return roomUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored conference id.
|
||||
*
|
||||
* @param {Object | Function} stateful - The Object or Function that can be
|
||||
* resolved to a Redux state object with the toState function.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getConferenceId(stateful: Object | Function) {
|
||||
return toState(stateful)['features/invite'].conferenceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default dial in number from the store.
|
||||
*
|
||||
* @param {Object | Function} stateful - The Object or Function that can be
|
||||
* resolved to a Redux state object with the toState function.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function getDefaultDialInNumber(stateful: Object | Function) {
|
||||
return _getDefaultPhoneNumber(toState(stateful)['features/invite'].numbers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the dial out request.
|
||||
*
|
||||
* @param {string} url - The url for dialing out.
|
||||
* @param {Object} body - The body of the request.
|
||||
* @param {string} reqId - The unique request id.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export async function executeDialOutRequest(url: string, body: Object, reqId: string) {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'request-id': reqId
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
return res.ok ? json : Promise.reject(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the dial out status request.
|
||||
*
|
||||
* @param {string} url - The url for dialing out.
|
||||
* @param {string} reqId - The unique request id used on the dial out request.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export async function executeDialOutStatusRequest(url: string, reqId: string) {
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'request-id': reqId
|
||||
}
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
return res.ok ? json : Promise.reject(json);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ const DEFAULT_STATE = {
|
|||
*/
|
||||
calleeInfoVisible: false,
|
||||
numbersEnabled: true,
|
||||
numbersFetched: false,
|
||||
pendingInviteRequests: []
|
||||
};
|
||||
|
||||
|
@ -59,7 +60,8 @@ ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
|
|||
...state,
|
||||
conferenceID: action.conferenceID,
|
||||
numbers: action.dialInNumbers,
|
||||
numbersEnabled: true
|
||||
numbersEnabled: true,
|
||||
numbersFetched: true
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -72,7 +74,8 @@ ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
|
|||
...state,
|
||||
conferenceID: action.conferenceID,
|
||||
numbers: action.dialInNumbers,
|
||||
numbersEnabled
|
||||
numbersEnabled,
|
||||
numbersFetched: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,21 @@ export const SET_DEVICE_STATUS = 'SET_DEVICE_STATUS';
|
|||
*/
|
||||
export const SET_SKIP_PREJOIN = 'SET_SKIP_PREJOIN';
|
||||
|
||||
/**
|
||||
* Action type to set the country to dial out to.
|
||||
*/
|
||||
export const SET_DIALOUT_COUNTRY = 'SET_DIALOUT_COUNTRY';
|
||||
|
||||
/**
|
||||
* Action type to set the dial out number.
|
||||
*/
|
||||
export const SET_DIALOUT_NUMBER = 'SET_DIALOUT_NUMBER';
|
||||
|
||||
/**
|
||||
* Action type to set the dial out status while dialing.
|
||||
*/
|
||||
export const SET_DIALOUT_STATUS = 'SET_DIALOUT_STATUS';
|
||||
|
||||
/**
|
||||
* Action type to set the visiblity of the 'JoinByPhone' dialog.
|
||||
*/
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { getRoomName } from '../base/conference';
|
||||
import {
|
||||
ADD_PREJOIN_AUDIO_TRACK,
|
||||
ADD_PREJOIN_CONTENT_SHARING_TRACK,
|
||||
ADD_PREJOIN_VIDEO_TRACK,
|
||||
PREJOIN_START_CONFERENCE,
|
||||
SET_DEVICE_STATUS,
|
||||
SET_DIALOUT_COUNTRY,
|
||||
SET_DIALOUT_NUMBER,
|
||||
SET_DIALOUT_STATUS,
|
||||
SET_SKIP_PREJOIN,
|
||||
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
|
||||
SET_PREJOIN_AUDIO_DISABLED,
|
||||
|
@ -15,9 +21,44 @@ import {
|
|||
SET_PREJOIN_VIDEO_DISABLED,
|
||||
SET_PREJOIN_VIDEO_MUTED
|
||||
} from './actionTypes';
|
||||
import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions';
|
||||
import { createLocalTrack } from '../base/lib-jitsi-meet';
|
||||
import { getAudioTrack, getVideoTrack } from './functions';
|
||||
import { openURLInBrowser } from '../base/util';
|
||||
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
|
||||
import logger from './logger';
|
||||
import { showErrorNotification } from '../notifications';
|
||||
|
||||
import {
|
||||
getFullDialOutNumber,
|
||||
getAudioTrack,
|
||||
getDialOutConferenceUrl,
|
||||
getDialOutCountry,
|
||||
getVideoTrack,
|
||||
isJoinByPhoneDialogVisible
|
||||
} from './functions';
|
||||
|
||||
const dialOutStatusToKeyMap = {
|
||||
INITIATED: 'presenceStatus.calling',
|
||||
RINGING: 'presenceStatus.ringing'
|
||||
};
|
||||
|
||||
const DIAL_OUT_STATUS = {
|
||||
INITIATED: 'INITIATED',
|
||||
RINGING: 'RINGING',
|
||||
CONNECTED: 'CONNECTED',
|
||||
DISCONNECTED: 'DISCONNECTED',
|
||||
FAILED: 'FAILED'
|
||||
};
|
||||
|
||||
/**
|
||||
* The time interval used between requests while polling for dial out status.
|
||||
*/
|
||||
const STATUS_REQ_FREQUENCY = 2000;
|
||||
|
||||
/**
|
||||
* The maximum number of retries while polling for dial out status.
|
||||
*/
|
||||
const STATUS_REQ_CAP = 45;
|
||||
|
||||
/**
|
||||
* Action used to add an audio track to the store.
|
||||
|
@ -58,6 +99,129 @@ export function addPrejoinContentSharingTrack(value: Object) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for status change after dial out.
|
||||
* Changes dialog message based on response, closes the dialog if there is an error,
|
||||
* joins the meeting when CONNECTED.
|
||||
*
|
||||
* @param {string} reqId - The request id used to correlate the dial out request with this one.
|
||||
* @param {Function} onSuccess - Success handler.
|
||||
* @param {Function} onFail - Fail handler.
|
||||
* @param {number} count - The number of retried calls. When it hits STATUS_REQ_CAP it should no longer make requests.
|
||||
* @returns {Function}
|
||||
*/
|
||||
function pollForStatus(
|
||||
reqId: string,
|
||||
onSuccess: Function,
|
||||
onFail: Function,
|
||||
count = 0) {
|
||||
return async function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
|
||||
try {
|
||||
if (!isJoinByPhoneDialogVisible(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await executeDialOutStatusRequest(getDialOutStatusUrl(state), reqId);
|
||||
|
||||
switch (res) {
|
||||
case DIAL_OUT_STATUS.INITIATED:
|
||||
case DIAL_OUT_STATUS.RINGING: {
|
||||
dispatch(setDialOutStatus(dialOutStatusToKeyMap[res]));
|
||||
|
||||
if (count < STATUS_REQ_CAP) {
|
||||
return setTimeout(() => {
|
||||
dispatch(pollForStatus(reqId, onSuccess, onFail, count + 1));
|
||||
}, STATUS_REQ_FREQUENCY);
|
||||
}
|
||||
|
||||
return onFail();
|
||||
}
|
||||
|
||||
case DIAL_OUT_STATUS.CONNECTED: {
|
||||
return onSuccess();
|
||||
}
|
||||
|
||||
case DIAL_OUT_STATUS.DISCONNECTED: {
|
||||
dispatch(showErrorNotification({
|
||||
titleKey: 'prejoin.errorDialOutDisconnected'
|
||||
}));
|
||||
|
||||
return onFail();
|
||||
}
|
||||
|
||||
case DIAL_OUT_STATUS.FAILED: {
|
||||
dispatch(showErrorNotification({
|
||||
titleKey: 'prejoin.errorDialOutFailed'
|
||||
}));
|
||||
|
||||
return onFail();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
dispatch(showErrorNotification({
|
||||
titleKey: 'prejoin.errorDialOutStatus'
|
||||
}));
|
||||
logger.error('Error getting dial out status', err);
|
||||
onFail();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Action used for joining the meeting with phone audio.
|
||||
* A dial out connection is tried and a polling mechanism is used for getting the status.
|
||||
* If the connection succeeds the `onSuccess` callback is executed.
|
||||
* If the phone connection fails or the number is invalid the `onFail` callback is executed.
|
||||
*
|
||||
* @param {Function} onSuccess - Success handler.
|
||||
* @param {Function} onFail - Fail handler.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function dialOut(onSuccess: Function, onFail: Function) {
|
||||
return async function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
const reqId = uuid.v4();
|
||||
const url = getDialOutUrl(state);
|
||||
const conferenceUrl = getDialOutConferenceUrl(state);
|
||||
const phoneNumber = getFullDialOutNumber(state);
|
||||
const countryCode = getDialOutCountry(state).code.toUpperCase();
|
||||
|
||||
const body = {
|
||||
conferenceUrl,
|
||||
countryCode,
|
||||
name: phoneNumber,
|
||||
phoneNumber
|
||||
};
|
||||
|
||||
try {
|
||||
await executeDialOutRequest(url, body, reqId);
|
||||
|
||||
dispatch(pollForStatus(reqId, onSuccess, onFail));
|
||||
} catch (err) {
|
||||
const notification = {
|
||||
titleKey: 'prejoin.errorDialOut',
|
||||
titleArguments: undefined
|
||||
};
|
||||
|
||||
if (err.status) {
|
||||
if (err.messageKey === 'validation.failed') {
|
||||
notification.titleKey = 'prejoin.errorValidation';
|
||||
} else {
|
||||
notification.titleKey = 'prejoin.errorStatusCode';
|
||||
notification.titleArguments = { status: err.status };
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(showErrorNotification(notification));
|
||||
logger.error('Error dialing out', err);
|
||||
onFail();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the newly created tracks to store on init.
|
||||
*
|
||||
|
@ -121,6 +285,22 @@ export function joinConferenceWithoutAudio() {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an external page with all the dial in numbers.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function openDialInPage() {
|
||||
return function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
const locationURL = state['features/base/connection'].locationURL;
|
||||
const roomName = getRoomName(state);
|
||||
const dialInPage = getDialInfoPageURL(roomName, locationURL);
|
||||
|
||||
openURLInBrowser(dialInPage, true);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the existing audio track with a new one.
|
||||
*
|
||||
|
@ -273,6 +453,45 @@ export function setDeviceStatusWarning(deviceStatusText: string) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to set the dial out status.
|
||||
*
|
||||
* @param {string} value - The status.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function setDialOutStatus(value: string) {
|
||||
return {
|
||||
type: SET_DIALOUT_STATUS,
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to set the dial out country.
|
||||
*
|
||||
* @param {{ name: string, dialCode: string, code: string }} value - The country.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function setDialOutCountry(value: Object) {
|
||||
return {
|
||||
type: SET_DIALOUT_COUNTRY,
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to set the dial out number.
|
||||
*
|
||||
* @param {string} value - The dial out number.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function setDialOutNumber(value: string) {
|
||||
return {
|
||||
type: SET_DIALOUT_NUMBER,
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of the prejoin page for future uses.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The text for the Label.
|
||||
*/
|
||||
children: React$Node,
|
||||
|
||||
/**
|
||||
* The CSS class of the label.
|
||||
*/
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* The (round) number prefix for the Label.
|
||||
*/
|
||||
number?: string | number,
|
||||
|
||||
/**
|
||||
* The click handler.
|
||||
*/
|
||||
onClick?: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* Label for the dialogs.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function Label({ children, className, number, onClick }: Props) {
|
||||
const containerClass = className
|
||||
? `prejoin-dialog-label ${className}`
|
||||
: 'prejoin-dialog-label';
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { containerClass }
|
||||
onClick = { onClick }>
|
||||
{number && <div className = 'prejoin-dialog-label-num'>{number}</div>}
|
||||
<span>{children}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Label;
|
|
@ -25,6 +25,8 @@ import DeviceStatus from './preview/DeviceStatus';
|
|||
import ParticipantName from './preview/ParticipantName';
|
||||
import Preview from './preview/Preview';
|
||||
import { VideoSettingsButton, AudioSettingsButton } from '../../toolbox';
|
||||
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
|
@ -112,6 +114,8 @@ class Prejoin extends Component<Props, State> {
|
|||
this.state = {
|
||||
showJoinByPhoneButtons: false
|
||||
};
|
||||
|
||||
this._closeDialog = this._closeDialog.bind(this);
|
||||
this._showDialog = this._showDialog.bind(this);
|
||||
this._onCheckboxChange = this._onCheckboxChange.bind(this);
|
||||
this._onDropdownClose = this._onDropdownClose.bind(this);
|
||||
|
@ -174,6 +178,17 @@ class Prejoin extends Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
_closeDialog: () => void;
|
||||
|
||||
/**
|
||||
* Closes the join by phone dialog.
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_closeDialog() {
|
||||
this.props.setJoinByPhoneDialogVisiblity(false);
|
||||
}
|
||||
|
||||
_showDialog: () => void;
|
||||
|
||||
/**
|
||||
|
@ -183,6 +198,7 @@ class Prejoin extends Component<Props, State> {
|
|||
*/
|
||||
_showDialog() {
|
||||
this.props.setJoinByPhoneDialogVisiblity(true);
|
||||
this._onDropdownClose();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,9 +215,11 @@ class Prejoin extends Component<Props, State> {
|
|||
joinConferenceWithoutAudio,
|
||||
name,
|
||||
hasJoinByPhoneButtons,
|
||||
showDialog,
|
||||
t
|
||||
} = this.props;
|
||||
const { _onCheckboxChange, _onDropdownClose, _onOptionsClick, _setName, _showDialog } = this;
|
||||
|
||||
const { _closeDialog, _onCheckboxChange, _onDropdownClose, _onOptionsClick, _setName, _showDialog } = this;
|
||||
const { showJoinByPhoneButtons } = this.state;
|
||||
|
||||
return (
|
||||
|
@ -271,6 +289,11 @@ class Prejoin extends Component<Props, State> {
|
|||
</div>
|
||||
|
||||
{ deviceStatusVisible && <DeviceStatus /> }
|
||||
{ showDialog && (
|
||||
<JoinByPhoneDialog
|
||||
joinConferenceWithoutAudio = { joinConferenceWithoutAudio }
|
||||
onClose = { _closeDialog } />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { countries } from '../../utils';
|
||||
import CountryRow from './CountryRow';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Click handler for a single entry.
|
||||
*/
|
||||
onEntryClick: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays the dropdown for the country picker.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function CountryDropdown({ onEntryClick }: Props) {
|
||||
return (
|
||||
<div className = 'cpick-dropdown'>
|
||||
{countries.map(country => (
|
||||
<CountryRow
|
||||
country = { country }
|
||||
key = { `${country.code}` }
|
||||
onEntryClick = { onEntryClick } />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CountryDropdown;
|
|
@ -0,0 +1,250 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import InlineDialog from '@atlaskit/inline-dialog';
|
||||
|
||||
import { connect } from '../../../base/redux';
|
||||
import { setDialOutCountry, setDialOutNumber } from '../../actions';
|
||||
import { getDialOutCountry, getDialOutNumber } from '../../functions';
|
||||
import { getCountryFromDialCodeText } from '../../utils';
|
||||
|
||||
import CountrySelector from './CountrySelector';
|
||||
import CountryDropDown from './CountryDropdown';
|
||||
|
||||
const PREFIX_REG = /^(00)|\+/;
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The country to dial out to.
|
||||
*/
|
||||
dialOutCountry: { name: string, dialCode: string, code: string },
|
||||
|
||||
/**
|
||||
* The number to dial out to.
|
||||
*/
|
||||
dialOutNumber: string,
|
||||
|
||||
/**
|
||||
* Handler used when user presses 'Enter'.
|
||||
*/
|
||||
onSubmit: Function,
|
||||
|
||||
/**
|
||||
* Sets the dial out number.
|
||||
*/
|
||||
setDialOutNumber: Function,
|
||||
|
||||
/**
|
||||
* Sets the dial out country.
|
||||
*/
|
||||
setDialOutCountry: Function,
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* If the country picker is open or not.
|
||||
*/
|
||||
isOpen: boolean,
|
||||
|
||||
/**
|
||||
* The value of the input.
|
||||
*/
|
||||
value: string
|
||||
}
|
||||
|
||||
/**
|
||||
* This component displays a country picker with an input for the phone number.
|
||||
*/
|
||||
class CountryPicker extends PureComponent<Props, State> {
|
||||
/**
|
||||
* A React ref to the HTML element containing the {@code input} instance.
|
||||
*/
|
||||
inputRef: Object;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code CountryPicker} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
value: ''
|
||||
};
|
||||
this.inputRef = React.createRef();
|
||||
this._onChange = this._onChange.bind(this);
|
||||
this._onDropdownClose = this._onDropdownClose.bind(this);
|
||||
this._onCountrySelectorClick = this._onCountrySelectorClick.bind(this);
|
||||
this._onEntryClick = this._onEntryClick.bind(this);
|
||||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidUnmount()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.inputRef.current.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { dialOutCountry, dialOutNumber } = this.props;
|
||||
const { isOpen } = this.state;
|
||||
const {
|
||||
inputRef,
|
||||
_onChange,
|
||||
_onCountrySelectorClick,
|
||||
_onDropdownClose,
|
||||
_onKeyPress,
|
||||
_onEntryClick
|
||||
} = this;
|
||||
|
||||
return (
|
||||
<div className = 'cpick-container'>
|
||||
<InlineDialog
|
||||
content = { <CountryDropDown onEntryClick = { _onEntryClick } /> }
|
||||
isOpen = { isOpen }
|
||||
onClose = { _onDropdownClose }>
|
||||
<div className = 'cpick'>
|
||||
<CountrySelector
|
||||
country = { dialOutCountry }
|
||||
onClick = { _onCountrySelectorClick } />
|
||||
<input
|
||||
className = 'cpick-input'
|
||||
onChange = { _onChange }
|
||||
onKeyPress = { _onKeyPress }
|
||||
ref = { inputRef }
|
||||
value = { dialOutNumber } />
|
||||
</div>
|
||||
</InlineDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_onChange: (Object) => void;
|
||||
|
||||
/**
|
||||
* Handles the input text change.
|
||||
* Automatically updates the country from the 'CountrySelector' if a
|
||||
* phone number prefix is entered (00 or +).
|
||||
*
|
||||
* @param {Object} e - The synthetic event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChange({ target: { value } }) {
|
||||
if (PREFIX_REG.test(value)) {
|
||||
const textWithDialCode = value.replace(PREFIX_REG, '');
|
||||
|
||||
if (textWithDialCode.length >= 4) {
|
||||
const country = getCountryFromDialCodeText(textWithDialCode);
|
||||
|
||||
if (country) {
|
||||
const rest = textWithDialCode.replace(country.dialCode, '');
|
||||
|
||||
this.props.setDialOutCountry(country);
|
||||
this.props.setDialOutNumber(rest);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.props.setDialOutNumber(value);
|
||||
}
|
||||
|
||||
_onCountrySelectorClick: (Object) => void;
|
||||
|
||||
/**
|
||||
* Click handler for country selector.
|
||||
*
|
||||
* @param {Object} e - The synthetic event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onCountrySelectorClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
this.setState({
|
||||
isOpen: !this.setState.isOpen
|
||||
});
|
||||
}
|
||||
|
||||
_onDropdownClose: () => void;
|
||||
|
||||
/**
|
||||
* Closes the dropdown.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDropdownClose() {
|
||||
this.setState({
|
||||
isOpen: false
|
||||
});
|
||||
}
|
||||
|
||||
_onEntryClick: (Object) => void;
|
||||
|
||||
/**
|
||||
* Click handler for a single entry from the dropdown.
|
||||
*
|
||||
* @param {Object} country - The country used for dialing out.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onEntryClick(country) {
|
||||
this.props.setDialOutCountry(country);
|
||||
this._onDropdownClose();
|
||||
}
|
||||
|
||||
_onKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* Handler for key presses.
|
||||
*
|
||||
* @param {Object} e - The synthetic event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(e) {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSubmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
dialOutCountry: getDialOutCountry(state),
|
||||
dialOutNumber: getDialOutNumber(state)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps redux actions to the props of the component.
|
||||
*
|
||||
* @type {{
|
||||
* setDialOutCountry: Function,
|
||||
* setDialOutNumber: Function
|
||||
* }}
|
||||
*/
|
||||
const mapDispatchToProps = {
|
||||
setDialOutCountry,
|
||||
setDialOutNumber
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CountryPicker);
|
|
@ -0,0 +1,68 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Country of the entry.
|
||||
*/
|
||||
country: { name: string, dialCode: string, code: string },
|
||||
|
||||
/**
|
||||
* Entry click handler.
|
||||
*/
|
||||
onEntryClick: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays a row from the country picker dropdown.
|
||||
*/
|
||||
class CountryRow extends PureComponent<Props> {
|
||||
/**
|
||||
* Initializes a new {@code CountryRow} instance.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onClick = this._onClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
country: { code, dialCode, name }
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'cpick-dropdown-entry'
|
||||
onClick = { this._onClick }>
|
||||
<div className = { `prejoin-dialog-flag iti-flag ${code}` } />
|
||||
<div className = 'cpick-dropdown-entry-text'>
|
||||
{`${name} (+${dialCode})`}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_onClick: () => void;
|
||||
|
||||
/**
|
||||
* Click handler.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClick() {
|
||||
this.props.onEntryClick(this.props.country);
|
||||
}
|
||||
}
|
||||
|
||||
export default CountryRow;
|
|
@ -0,0 +1,39 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Icon, IconArrowDown } from '../../../base/icons';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Country object of the entry.
|
||||
*/
|
||||
country: { name: string, dialCode: string, code: string },
|
||||
|
||||
/**
|
||||
* Click handler for the selector.
|
||||
*/
|
||||
onClick: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays the country selector with the flag.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function CountrySelector({ country: { code, dialCode }, onClick }: Props) {
|
||||
return (
|
||||
<div
|
||||
className = 'cpick-selector'
|
||||
onClick = { onClick }>
|
||||
<div className = { `prejoin-dialog-flag iti-flag ${code}` } />
|
||||
<span>{`+${dialCode}`}</span>
|
||||
<Icon
|
||||
className = 'cpick-icon'
|
||||
size = { 16 }
|
||||
src = { IconArrowDown } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CountrySelector;
|
|
@ -0,0 +1,64 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconClose } from '../../../base/icons';
|
||||
import Label from '../Label';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The phone number that is being called.
|
||||
*/
|
||||
number: string,
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*/
|
||||
onClose: Function,
|
||||
|
||||
/**
|
||||
* Handler used on hangup click.
|
||||
*/
|
||||
onHangup: Function,
|
||||
|
||||
/**
|
||||
* The status of the call.
|
||||
*/
|
||||
status: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* Dialog displayed when the user gets called by the meeting.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function CallingDialog(props: Props) {
|
||||
const { number, onClose, status, t } = props;
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-dialog-calling'>
|
||||
<div className = 'prejoin-dialog-calling-header'>
|
||||
<Icon
|
||||
className = 'prejoin-dialog-icon'
|
||||
onClick = { onClose }
|
||||
size = { 24 }
|
||||
src = { IconClose } />
|
||||
</div>
|
||||
<Label className = 'prejoin-dialog-calling-label'>
|
||||
{t(status)}
|
||||
</Label>
|
||||
<Avatar size = { 72 } />
|
||||
<div className = 'prejoin-dialog-calling-number'>{number}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(CallingDialog);
|
|
@ -0,0 +1,121 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconArrowLeft } from '../../../base/icons';
|
||||
import { getCountryCodeFromPhone } from '../../utils';
|
||||
import ActionButton from '../buttons/ActionButton';
|
||||
import Label from '../Label';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The number to call in order to join the conference.
|
||||
*/
|
||||
number: string,
|
||||
|
||||
/**
|
||||
* Handler used when clicking the back button.
|
||||
*/
|
||||
onBack: Function,
|
||||
|
||||
/**
|
||||
* Click handler for the text button.
|
||||
*/
|
||||
onTextButtonClick: Function,
|
||||
|
||||
/**
|
||||
* Click handler for primary button.
|
||||
*/
|
||||
onPrimaryButtonClick: Function,
|
||||
|
||||
/**
|
||||
* Click handler for the small additional text.
|
||||
*/
|
||||
onSmallTextClick: Function,
|
||||
|
||||
/**
|
||||
* The passCode of the conference.
|
||||
*/
|
||||
passCode: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays the dialog whith all the information
|
||||
* to join a meeting by calling it.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
function DialinDialog(props: Props) {
|
||||
const {
|
||||
number,
|
||||
onBack,
|
||||
onPrimaryButtonClick,
|
||||
onSmallTextClick,
|
||||
onTextButtonClick,
|
||||
passCode,
|
||||
t
|
||||
} = props;
|
||||
const flagClassName = `prejoin-dialog-flag iti-flag ${getCountryCodeFromPhone(
|
||||
number,
|
||||
)}`;
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-dialog-dialin'>
|
||||
<div className = 'prejoin-dialog-dialin-header'>
|
||||
<Icon
|
||||
className = 'prejoin-dialog-icon prejoin-dialog-dialin-icon'
|
||||
onClick = { onBack }
|
||||
size = { 24 }
|
||||
src = { IconArrowLeft } />
|
||||
<div className = 'prejoin-dialog-title'>
|
||||
{t('prejoin.dialInMeeting')}
|
||||
</div>
|
||||
</div>
|
||||
<Label number = { 1 }>{ t('prejoin.dialInPin') }</Label>
|
||||
|
||||
<div className = 'prejoin-dialog-dialin-num-container'>
|
||||
<div className = 'prejoin-dialog-dialin-num'>
|
||||
<div className = { flagClassName } />
|
||||
<span>{number}</span>
|
||||
</div>
|
||||
<div className = 'prejoin-dialog-dialin-num'>{passCode}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
className = 'prejoin-dialog-dialin-link'
|
||||
onClick = { onSmallTextClick }>
|
||||
{t('prejoin.viewAllNumbers')}
|
||||
</span>
|
||||
</div>
|
||||
<div className = 'prejoin-dialog-delimiter' />
|
||||
<Label
|
||||
className = 'prejoin-dialog-dialin-spaced-label'
|
||||
number = { 2 }>
|
||||
{t('prejoin.connectedWithAudioQ')}
|
||||
</Label>
|
||||
<div className = 'prejoin-dialog-dialin-btns'>
|
||||
<ActionButton
|
||||
className = 'prejoin-dialog-btn'
|
||||
onClick = { onPrimaryButtonClick }
|
||||
type = 'primary'>
|
||||
{t('prejoin.joinMeeting')}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
className = 'prejoin-dialog-btn'
|
||||
onClick = { onTextButtonClick }
|
||||
type = 'text'>
|
||||
{t('dialog.Cancel')}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(DialinDialog);
|
|
@ -0,0 +1,86 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconClose } from '../../../base/icons';
|
||||
import ActionButton from '../buttons/ActionButton';
|
||||
import CountryPicker from '../country-picker/CountryPicker';
|
||||
import Label from '../Label';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Closes a dialog.
|
||||
*/
|
||||
onClose: Function,
|
||||
|
||||
/**
|
||||
* Submit handler.
|
||||
*/
|
||||
onSubmit: Function,
|
||||
|
||||
/**
|
||||
* Handler for text button.
|
||||
*/
|
||||
onTextButtonClick: Function,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component displays the dialog from wich the user can enter the
|
||||
* phone number in order to be called by the meeting.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
function DialOutDialog(props: Props) {
|
||||
const { onClose, onTextButtonClick, onSubmit, t } = props;
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-dialog-callout'>
|
||||
<div className = 'prejoin-dialog-callout-header'>
|
||||
<div className = 'prejoin-dialog-title'>
|
||||
{t('prejoin.startWithPhone')}
|
||||
</div>
|
||||
<Icon
|
||||
className = 'prejoin-dialog-icon'
|
||||
onClick = { onClose }
|
||||
size = { 24 }
|
||||
src = { IconClose } />
|
||||
</div>
|
||||
<Label>{t('prejoin.callMeAtNumber')}</Label>
|
||||
<div className = 'prejoin-dialog-callout-picker'>
|
||||
<CountryPicker onSubmit = { onSubmit } />
|
||||
</div>
|
||||
<ActionButton
|
||||
className = 'prejoin-dialog-btn'
|
||||
onClick = { onSubmit }
|
||||
type = 'primary'>
|
||||
{t('prejoin.callMe')}
|
||||
</ActionButton>
|
||||
<div className = 'prejoin-dialog-delimiter-container'>
|
||||
<div className = 'prejoin-dialog-delimiter' />
|
||||
<div className = 'prejoin-dialog-delimiter-txt-container'>
|
||||
<span className = 'prejoin-dialog-delimiter-txt'>
|
||||
{t('prejoin.or')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className = 'prejoin-dialog-dialin-container'>
|
||||
<ActionButton
|
||||
className = 'prejoin-dialog-btn'
|
||||
onClick = { onTextButtonClick }
|
||||
type = 'text'>
|
||||
{t('prejoin.iWantToDialIn')}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(DialOutDialog);
|
|
@ -0,0 +1,248 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { connect } from '../../../base/redux';
|
||||
import {
|
||||
getConferenceId,
|
||||
getDefaultDialInNumber,
|
||||
updateDialInNumbers
|
||||
} from '../../../invite';
|
||||
import {
|
||||
dialOut as dialOutAction,
|
||||
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
|
||||
openDialInPage as openDialInPageAction
|
||||
} from '../../actions';
|
||||
import { getDialOutStatus, getFullDialOutNumber } from '../../functions';
|
||||
|
||||
import CallingDialog from './CallingDialog';
|
||||
import DialInDialog from './DialInDialog';
|
||||
import DialOutDialog from './DialOutDialog';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The number to call in order to join the conference.
|
||||
*/
|
||||
dialInNumber: string,
|
||||
|
||||
/**
|
||||
* The status of the call when the meeting calss the user.
|
||||
*/
|
||||
dialOutStatus: string,
|
||||
|
||||
/**
|
||||
* The action by which the meeting calls the user.
|
||||
*/
|
||||
dialOut: Function,
|
||||
|
||||
/**
|
||||
* The number the conference should call.
|
||||
*/
|
||||
dialOutNumber: string,
|
||||
|
||||
/**
|
||||
* Fetches conference dial in numbers & conference id
|
||||
*/
|
||||
fetchConferenceDetails: Function,
|
||||
|
||||
/**
|
||||
* Joins the conference without audio.
|
||||
*/
|
||||
joinConferenceWithoutAudio: Function,
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*/
|
||||
onClose: Function,
|
||||
|
||||
/**
|
||||
* Opens a web page with all the dial in numbers.
|
||||
*/
|
||||
openDialInPage: Function,
|
||||
|
||||
/**
|
||||
* The passCode of the conference used when joining a meeting by phone.
|
||||
*/
|
||||
passCode: string,
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* The dialout call is ongoing, 'CallingDialog' is shown;
|
||||
*/
|
||||
isCalling: boolean,
|
||||
|
||||
/**
|
||||
* If should show 'DialInDialog'.
|
||||
*/
|
||||
showDialIn: boolean,
|
||||
|
||||
/**
|
||||
* If should show 'DialOutDialog'.
|
||||
*/
|
||||
showDialOut: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the dialog shown when a user wants to join with phone audio.
|
||||
*/
|
||||
class JoinByPhoneDialog extends PureComponent<Props, State> {
|
||||
/**
|
||||
* Initializes a new {@code JoinByPhoneDialog} instance.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isCalling: false,
|
||||
showDialOut: true,
|
||||
showDialIn: false
|
||||
};
|
||||
|
||||
this._dialOut = this._dialOut.bind(this);
|
||||
this._showDialInDialog = this._showDialInDialog.bind(this);
|
||||
this._showDialOutDialog = this._showDialOutDialog.bind(this);
|
||||
}
|
||||
|
||||
_dialOut: () => void;
|
||||
|
||||
/**
|
||||
* Meeting calls the user & shows the 'CallingDialog'.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_dialOut() {
|
||||
const { dialOut, joinConferenceWithoutAudio } = this.props;
|
||||
|
||||
this.setState({
|
||||
isCalling: true,
|
||||
showDialOut: false,
|
||||
showDialIn: false
|
||||
});
|
||||
dialOut(joinConferenceWithoutAudio, this._showDialOutDialog);
|
||||
}
|
||||
|
||||
_showDialInDialog: () => void;
|
||||
|
||||
/**
|
||||
* Shows the 'DialInDialog'.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showDialInDialog() {
|
||||
this.setState({
|
||||
isCalling: false,
|
||||
showDialOut: false,
|
||||
showDialIn: true
|
||||
});
|
||||
}
|
||||
|
||||
_showDialOutDialog: () => void;
|
||||
|
||||
/**
|
||||
* Shows the 'DialOutDialog'.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showDialOutDialog() {
|
||||
this.setState({
|
||||
isCalling: false,
|
||||
showDialOut: true,
|
||||
showDialIn: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.fetchConferenceDetails();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
dialOutStatus,
|
||||
dialInNumber,
|
||||
dialOutNumber,
|
||||
joinConferenceWithoutAudio,
|
||||
passCode,
|
||||
onClose,
|
||||
openDialInPage
|
||||
} = this.props;
|
||||
const {
|
||||
_dialOut,
|
||||
_showDialInDialog,
|
||||
_showDialOutDialog
|
||||
} = this;
|
||||
const { isCalling, showDialOut, showDialIn } = this.state;
|
||||
const className = isCalling
|
||||
? 'prejoin-dialog prejoin-dialog--small'
|
||||
: 'prejoin-dialog';
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-dialog-container'>
|
||||
<div className = { className }>
|
||||
{showDialOut && (
|
||||
<DialOutDialog
|
||||
onClose = { onClose }
|
||||
onSubmit = { _dialOut }
|
||||
onTextButtonClick = { _showDialInDialog } />
|
||||
)}
|
||||
{showDialIn && (
|
||||
<DialInDialog
|
||||
number = { dialInNumber }
|
||||
onBack = { _showDialOutDialog }
|
||||
onPrimaryButtonClick = { joinConferenceWithoutAudio }
|
||||
onSmallTextClick = { openDialInPage }
|
||||
onTextButtonClick = { onClose }
|
||||
passCode = { passCode } />
|
||||
)}
|
||||
{isCalling && (
|
||||
<CallingDialog
|
||||
number = { dialOutNumber }
|
||||
onClose = { onClose }
|
||||
status = { dialOutStatus } />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state): Object {
|
||||
return {
|
||||
dialInNumber: getDefaultDialInNumber(state),
|
||||
dialOutNumber: getFullDialOutNumber(state),
|
||||
dialOutStatus: getDialOutStatus(state),
|
||||
passCode: getConferenceId(state)
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dialOut: dialOutAction,
|
||||
fetchConferenceDetails: updateDialInNumbers,
|
||||
joinConferenceWithoutAudio: joinConferenceWithoutAudioAction,
|
||||
openDialInPage: openDialInPageAction
|
||||
};
|
||||
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JoinByPhoneDialog);
|
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { getRoomName } from '../base/conference';
|
||||
import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions';
|
||||
|
||||
/**
|
||||
* Mutes or unmutes a track.
|
||||
|
@ -27,7 +29,7 @@ function applyMuteOptionsToTrack(track, shouldMute) {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
export function areJoinByPhoneButtonsVisible(state: Object): boolean {
|
||||
return state['features/prejoin'].buttonsVisible;
|
||||
return Boolean(getDialOutUrl(state) && getDialOutStatusUrl(state));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,6 +128,59 @@ export function getDeviceStatusType(state: Object): string {
|
|||
return state['features/prejoin']?.deviceStatusType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'conferenceUrl' used for dialing out.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDialOutConferenceUrl(state: Object): string {
|
||||
return `${getRoomName(state)}@${state['features/base/config'].hosts.muc}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for getting the dial out country.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getDialOutCountry(state: Object): Object {
|
||||
return state['features/prejoin'].dialOutCountry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for getting the dial out number (without prefix).
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDialOutNumber(state: Object): string {
|
||||
return state['features/prejoin'].dialOutNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for getting the dial out status while calling.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDialOutStatus(state: Object): string {
|
||||
return state['features/prejoin'].dialOutStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full dial out number (containing country code and +).
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getFullDialOutNumber(state: Object): string {
|
||||
const dialOutNumber = getDialOutNumber(state);
|
||||
const country = getDialOutCountry(state);
|
||||
|
||||
return `+${country.dialCode}${dialOutNumber}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for getting the prejoin video track.
|
||||
*
|
||||
|
|
|
@ -5,6 +5,9 @@ import {
|
|||
ADD_PREJOIN_CONTENT_SHARING_TRACK,
|
||||
ADD_PREJOIN_VIDEO_TRACK,
|
||||
SET_DEVICE_STATUS,
|
||||
SET_DIALOUT_NUMBER,
|
||||
SET_DIALOUT_COUNTRY,
|
||||
SET_DIALOUT_STATUS,
|
||||
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
|
||||
SET_SKIP_PREJOIN,
|
||||
SET_PREJOIN_AUDIO_DISABLED,
|
||||
|
@ -18,17 +21,26 @@ import {
|
|||
const DEFAULT_STATE = {
|
||||
audioDisabled: false,
|
||||
audioMuted: false,
|
||||
videoMuted: false,
|
||||
videoDisabled: false,
|
||||
audioTrack: null,
|
||||
contentSharingTrack: null,
|
||||
country: '',
|
||||
deviceStatusText: 'prejoin.configuringDevices',
|
||||
deviceStatusType: 'ok',
|
||||
dialOutCountry: {
|
||||
name: 'United States',
|
||||
dialCode: '1',
|
||||
code: 'us'
|
||||
},
|
||||
dialOutNumber: '',
|
||||
dialOutStatus: 'prejoin.dialing',
|
||||
name: '',
|
||||
rawError: '',
|
||||
showPrejoin: true,
|
||||
showJoinByPhoneDialog: false,
|
||||
userSelectedSkipPrejoin: false,
|
||||
videoTrack: null,
|
||||
audioTrack: null,
|
||||
contentSharingTrack: null,
|
||||
rawError: ''
|
||||
videoDisabled: false,
|
||||
videoMuted: false
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -114,6 +126,27 @@ ReducerRegistry.register(
|
|||
};
|
||||
}
|
||||
|
||||
case SET_DIALOUT_NUMBER: {
|
||||
return {
|
||||
...state,
|
||||
dialOutNumber: action.value
|
||||
};
|
||||
}
|
||||
|
||||
case SET_DIALOUT_COUNTRY: {
|
||||
return {
|
||||
...state,
|
||||
dialOutCountry: action.value
|
||||
};
|
||||
}
|
||||
|
||||
case SET_DIALOUT_STATUS: {
|
||||
return {
|
||||
...state,
|
||||
dialOutStatus: action.value
|
||||
};
|
||||
}
|
||||
|
||||
case SET_JOIN_BY_PHONE_DIALOG_VISIBLITY: {
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -0,0 +1,799 @@
|
|||
// @flow
|
||||
|
||||
export const countries = [
|
||||
{ name: 'Afghanistan',
|
||||
dialCode: '93',
|
||||
code: 'af' },
|
||||
{ name: 'Aland Islands',
|
||||
dialCode: '358',
|
||||
code: 'ax' },
|
||||
{ name: 'Albania',
|
||||
dialCode: '355',
|
||||
code: 'al' },
|
||||
{ name: 'Algeria',
|
||||
dialCode: '213',
|
||||
code: 'dz' },
|
||||
{ name: 'AmericanSamoa',
|
||||
dialCode: '1684',
|
||||
code: 'as' },
|
||||
{ name: 'Andorra',
|
||||
dialCode: '376',
|
||||
code: 'ad' },
|
||||
{ name: 'Angola',
|
||||
dialCode: '244',
|
||||
code: 'ao' },
|
||||
{ name: 'Anguilla',
|
||||
dialCode: '1264',
|
||||
code: 'ai' },
|
||||
{ name: 'Antarctica',
|
||||
dialCode: '672',
|
||||
code: 'aq' },
|
||||
{ name: 'Antigua and Barbuda',
|
||||
dialCode: '1268',
|
||||
code: 'ag' },
|
||||
{ name: 'Argentina',
|
||||
dialCode: '54',
|
||||
code: 'ar' },
|
||||
{ name: 'Armenia',
|
||||
dialCode: '374',
|
||||
code: 'am' },
|
||||
{ name: 'Aruba',
|
||||
dialCode: '297',
|
||||
code: 'aw' },
|
||||
{ name: 'Australia',
|
||||
dialCode: '61',
|
||||
code: 'au' },
|
||||
{ name: 'Austria',
|
||||
dialCode: '43',
|
||||
code: 'at' },
|
||||
{ name: 'Azerbaijan',
|
||||
dialCode: '994',
|
||||
code: 'az' },
|
||||
{ name: 'Bahamas',
|
||||
dialCode: '1242',
|
||||
code: 'bs' },
|
||||
{ name: 'Bahrain',
|
||||
dialCode: '973',
|
||||
code: 'bh' },
|
||||
{ name: 'Bangladesh',
|
||||
dialCode: '880',
|
||||
code: 'bd' },
|
||||
{ name: 'Barbados',
|
||||
dialCode: '1246',
|
||||
code: 'bb' },
|
||||
{ name: 'Belarus',
|
||||
dialCode: '375',
|
||||
code: 'by' },
|
||||
{ name: 'Belgium',
|
||||
dialCode: '32',
|
||||
code: 'be' },
|
||||
{ name: 'Belize',
|
||||
dialCode: '501',
|
||||
code: 'bz' },
|
||||
{ name: 'Benin',
|
||||
dialCode: '229',
|
||||
code: 'bj' },
|
||||
{ name: 'Bermuda',
|
||||
dialCode: '1441',
|
||||
code: 'bm' },
|
||||
{ name: 'Bhutan',
|
||||
dialCode: '975',
|
||||
code: 'bt' },
|
||||
{ name: 'Bolivia, Plurinational State of',
|
||||
dialCode: '591',
|
||||
code: 'bo' },
|
||||
{ name: 'Bosnia and Herzegovina',
|
||||
dialCode: '387',
|
||||
code: 'ba' },
|
||||
{ name: 'Botswana',
|
||||
dialCode: '267',
|
||||
code: 'bw' },
|
||||
{ name: 'Brazil',
|
||||
dialCode: '55',
|
||||
code: 'br' },
|
||||
{ name: 'British Indian Ocean Territory',
|
||||
dialCode: '246',
|
||||
code: 'io' },
|
||||
{ name: 'Brunei Darussalam',
|
||||
dialCode: '673',
|
||||
code: 'bn' },
|
||||
{ name: 'Bulgaria',
|
||||
dialCode: '359',
|
||||
code: 'bg' },
|
||||
{ name: 'Burkina Faso',
|
||||
dialCode: '226',
|
||||
code: 'bf' },
|
||||
{ name: 'Burundi',
|
||||
dialCode: '257',
|
||||
code: 'bi' },
|
||||
{ name: 'Cambodia',
|
||||
dialCode: '855',
|
||||
code: 'kh' },
|
||||
{ name: 'Cameroon',
|
||||
dialCode: '237',
|
||||
code: 'cm' },
|
||||
{ name: 'Canada',
|
||||
dialCode: '1',
|
||||
code: 'ca' },
|
||||
{ name: 'Cape Verde',
|
||||
dialCode: '238',
|
||||
code: 'cv' },
|
||||
{ name: 'Cayman Islands',
|
||||
dialCode: ' 345',
|
||||
code: 'ky' },
|
||||
{ name: 'Central African Republic',
|
||||
dialCode: '236',
|
||||
code: 'cf' },
|
||||
{ name: 'Chad',
|
||||
dialCode: '235',
|
||||
code: 'td' },
|
||||
{ name: 'Chile',
|
||||
dialCode: '56',
|
||||
code: 'cl' },
|
||||
{ name: 'China',
|
||||
dialCode: '86',
|
||||
code: 'cn' },
|
||||
{ name: 'Christmas Island',
|
||||
dialCode: '61',
|
||||
code: 'cx' },
|
||||
{ name: 'Cocos (Keeling) Islands',
|
||||
dialCode: '61',
|
||||
code: 'cc' },
|
||||
{ name: 'Colombia',
|
||||
dialCode: '57',
|
||||
code: 'co' },
|
||||
{ name: 'Comoros',
|
||||
dialCode: '269',
|
||||
code: 'km' },
|
||||
{ name: 'Congo',
|
||||
dialCode: '242',
|
||||
code: 'cg' },
|
||||
{
|
||||
name: 'Congo, The Democratic Republic of the Congo',
|
||||
dialCode: '243',
|
||||
code: 'cd'
|
||||
},
|
||||
{ name: 'Cook Islands',
|
||||
dialCode: '682',
|
||||
code: 'ck' },
|
||||
{ name: 'Costa Rica',
|
||||
dialCode: '506',
|
||||
code: 'cr' },
|
||||
{ name: 'Cote d\'Ivoire',
|
||||
dialCode: '225',
|
||||
code: 'ci' },
|
||||
{ name: 'Croatia',
|
||||
dialCode: '385',
|
||||
code: 'hr' },
|
||||
{ name: 'Cuba',
|
||||
dialCode: '53',
|
||||
code: 'cu' },
|
||||
{ name: 'Cyprus',
|
||||
dialCode: '357',
|
||||
code: 'cy' },
|
||||
{ name: 'Czech Republic',
|
||||
dialCode: '420',
|
||||
code: 'cz' },
|
||||
{ name: 'Denmark',
|
||||
dialCode: '45',
|
||||
code: 'dk' },
|
||||
{ name: 'Djibouti',
|
||||
dialCode: '253',
|
||||
code: 'dj' },
|
||||
{ name: 'Dominica',
|
||||
dialCode: '1767',
|
||||
code: 'dm' },
|
||||
{ name: 'Dominican Republic',
|
||||
dialCode: '1849',
|
||||
code: 'do' },
|
||||
{ name: 'Ecuador',
|
||||
dialCode: '593',
|
||||
code: 'ec' },
|
||||
{ name: 'Egypt',
|
||||
dialCode: '20',
|
||||
code: 'eg' },
|
||||
{ name: 'El Salvador',
|
||||
dialCode: '503',
|
||||
code: 'sv' },
|
||||
{ name: 'Equatorial Guinea',
|
||||
dialCode: '240',
|
||||
code: 'gq' },
|
||||
{ name: 'Eritrea',
|
||||
dialCode: '291',
|
||||
code: 'er' },
|
||||
{ name: 'Estonia',
|
||||
dialCode: '372',
|
||||
code: 'ee' },
|
||||
{ name: 'Ethiopia',
|
||||
dialCode: '251',
|
||||
code: 'et' },
|
||||
{ name: 'Falkland Islands (Malvinas)',
|
||||
dialCode: '500',
|
||||
code: 'fk' },
|
||||
{ name: 'Faroe Islands',
|
||||
dialCode: '298',
|
||||
code: 'fo' },
|
||||
{ name: 'Fiji',
|
||||
dialCode: '679',
|
||||
code: 'fj' },
|
||||
{ name: 'Finland',
|
||||
dialCode: '358',
|
||||
code: 'fi' },
|
||||
{ name: 'France',
|
||||
dialCode: '33',
|
||||
code: 'fr' },
|
||||
{ name: 'French Guiana',
|
||||
dialCode: '594',
|
||||
code: 'gf' },
|
||||
{ name: 'French Polynesia',
|
||||
dialCode: '689',
|
||||
code: 'pf' },
|
||||
{ name: 'Gabon',
|
||||
dialCode: '241',
|
||||
code: 'ga' },
|
||||
{ name: 'Gambia',
|
||||
dialCode: '220',
|
||||
code: 'gm' },
|
||||
{ name: 'Georgia',
|
||||
dialCode: '995',
|
||||
code: 'ge' },
|
||||
{ name: 'Germany',
|
||||
dialCode: '49',
|
||||
code: 'de' },
|
||||
{ name: 'Ghana',
|
||||
dialCode: '233',
|
||||
code: 'gh' },
|
||||
{ name: 'Gibraltar',
|
||||
dialCode: '350',
|
||||
code: 'gi' },
|
||||
{ name: 'Greece',
|
||||
dialCode: '30',
|
||||
code: 'gr' },
|
||||
{ name: 'Greenland',
|
||||
dialCode: '299',
|
||||
code: 'gl' },
|
||||
{ name: 'Grenada',
|
||||
dialCode: '1473',
|
||||
code: 'gd' },
|
||||
{ name: 'Guadeloupe',
|
||||
dialCode: '590',
|
||||
code: 'gp' },
|
||||
{ name: 'Guam',
|
||||
dialCode: '1671',
|
||||
code: 'gu' },
|
||||
{ name: 'Guatemala',
|
||||
dialCode: '502',
|
||||
code: 'gt' },
|
||||
{ name: 'Guernsey',
|
||||
dialCode: '44',
|
||||
code: 'gg' },
|
||||
{ name: 'Guinea',
|
||||
dialCode: '224',
|
||||
code: 'gn' },
|
||||
{ name: 'Guinea-Bissau',
|
||||
dialCode: '245',
|
||||
code: 'gw' },
|
||||
{ name: 'Guyana',
|
||||
dialCode: '595',
|
||||
code: 'gy' },
|
||||
{ name: 'Haiti',
|
||||
dialCode: '509',
|
||||
code: 'ht' },
|
||||
{ name: 'Holy See (Vatican City State)',
|
||||
dialCode: '379',
|
||||
code: 'va' },
|
||||
{ name: 'Honduras',
|
||||
dialCode: '504',
|
||||
code: 'hn' },
|
||||
{ name: 'Hong Kong',
|
||||
dialCode: '852',
|
||||
code: 'hk' },
|
||||
{ name: 'Hungary',
|
||||
dialCode: '36',
|
||||
code: 'hu' },
|
||||
{ name: 'Iceland',
|
||||
dialCode: '354',
|
||||
code: 'is' },
|
||||
{ name: 'India',
|
||||
dialCode: '91',
|
||||
code: 'in' },
|
||||
{ name: 'Indonesia',
|
||||
dialCode: '62',
|
||||
code: 'id' },
|
||||
{
|
||||
name: 'Iran, Islamic Republic of Persian Gulf',
|
||||
dialCode: '98',
|
||||
code: 'ir'
|
||||
},
|
||||
{ name: 'Iraq',
|
||||
dialCode: '964',
|
||||
code: 'iq' },
|
||||
{ name: 'Ireland',
|
||||
dialCode: '353',
|
||||
code: 'ie' },
|
||||
{ name: 'Isle of Man',
|
||||
dialCode: '44',
|
||||
code: 'im' },
|
||||
{ name: 'Israel',
|
||||
dialCode: '972',
|
||||
code: 'il' },
|
||||
{ name: 'Italy',
|
||||
dialCode: '39',
|
||||
code: 'it' },
|
||||
{ name: 'Jamaica',
|
||||
dialCode: '1876',
|
||||
code: 'jm' },
|
||||
{ name: 'Japan',
|
||||
dialCode: '81',
|
||||
code: 'jp' },
|
||||
{ name: 'Jersey',
|
||||
dialCode: '44',
|
||||
code: 'je' },
|
||||
{ name: 'Jordan',
|
||||
dialCode: '962',
|
||||
code: 'jo' },
|
||||
{ name: 'Kazakhstan',
|
||||
dialCode: '77',
|
||||
code: 'kz' },
|
||||
{ name: 'Kenya',
|
||||
dialCode: '254',
|
||||
code: 'ke' },
|
||||
{ name: 'Kiribati',
|
||||
dialCode: '686',
|
||||
code: 'ki' },
|
||||
{
|
||||
name: 'Korea, Democratic People\'s Republic of Korea',
|
||||
dialCode: '850',
|
||||
code: 'kp'
|
||||
},
|
||||
{ name: 'Korea, Republic of South Korea',
|
||||
dialCode: '82',
|
||||
code: 'kr' },
|
||||
{ name: 'Kuwait',
|
||||
dialCode: '965',
|
||||
code: 'kw' },
|
||||
{ name: 'Kyrgyzstan',
|
||||
dialCode: '996',
|
||||
code: 'kg' },
|
||||
{ name: 'Laos',
|
||||
dialCode: '856',
|
||||
code: 'la' },
|
||||
{ name: 'Latvia',
|
||||
dialCode: '371',
|
||||
code: 'lv' },
|
||||
{ name: 'Lebanon',
|
||||
dialCode: '961',
|
||||
code: 'lb' },
|
||||
{ name: 'Lesotho',
|
||||
dialCode: '266',
|
||||
code: 'ls' },
|
||||
{ name: 'Liberia',
|
||||
dialCode: '231',
|
||||
code: 'lr' },
|
||||
{ name: 'Libyan Arab Jamahiriya',
|
||||
dialCode: '218',
|
||||
code: 'ly' },
|
||||
{ name: 'Liechtenstein',
|
||||
dialCode: '423',
|
||||
code: 'li' },
|
||||
{ name: 'Lithuania',
|
||||
dialCode: '370',
|
||||
code: 'lt' },
|
||||
{ name: 'Luxembourg',
|
||||
dialCode: '352',
|
||||
code: 'lu' },
|
||||
{ name: 'Macao',
|
||||
dialCode: '853',
|
||||
code: 'mo' },
|
||||
{ name: 'Macedonia',
|
||||
dialCode: '389',
|
||||
code: 'mk' },
|
||||
{ name: 'Madagascar',
|
||||
dialCode: '261',
|
||||
code: 'mg' },
|
||||
{ name: 'Malawi',
|
||||
dialCode: '265',
|
||||
code: 'mw' },
|
||||
{ name: 'Malaysia',
|
||||
dialCode: '60',
|
||||
code: 'my' },
|
||||
{ name: 'Maldives',
|
||||
dialCode: '960',
|
||||
code: 'mv' },
|
||||
{ name: 'Mali',
|
||||
dialCode: '223',
|
||||
code: 'ml' },
|
||||
{ name: 'Malta',
|
||||
dialCode: '356',
|
||||
code: 'mt' },
|
||||
{ name: 'Marshall Islands',
|
||||
dialCode: '692',
|
||||
code: 'mh' },
|
||||
{ name: 'Martinique',
|
||||
dialCode: '596',
|
||||
code: 'mq' },
|
||||
{ name: 'Mauritania',
|
||||
dialCode: '222',
|
||||
code: 'mr' },
|
||||
{ name: 'Mauritius',
|
||||
dialCode: '230',
|
||||
code: 'mu' },
|
||||
{ name: 'Mayotte',
|
||||
dialCode: '262',
|
||||
code: 'yt' },
|
||||
{ name: 'Mexico',
|
||||
dialCode: '52',
|
||||
code: 'mx' },
|
||||
{
|
||||
name: 'Micronesia, Federated States of Micronesia',
|
||||
dialCode: '691',
|
||||
code: 'fm'
|
||||
},
|
||||
{ name: 'Moldova',
|
||||
dialCode: '373',
|
||||
code: 'md' },
|
||||
{ name: 'Monaco',
|
||||
dialCode: '377',
|
||||
code: 'mc' },
|
||||
{ name: 'Mongolia',
|
||||
dialCode: '976',
|
||||
code: 'mn' },
|
||||
{ name: 'Montenegro',
|
||||
dialCode: '382',
|
||||
code: 'me' },
|
||||
{ name: 'Montserrat',
|
||||
dialCode: '1664',
|
||||
code: 'ms' },
|
||||
{ name: 'Morocco',
|
||||
dialCode: '212',
|
||||
code: 'ma' },
|
||||
{ name: 'Mozambique',
|
||||
dialCode: '258',
|
||||
code: 'mz' },
|
||||
{ name: 'Myanmar',
|
||||
dialCode: '95',
|
||||
code: 'mm' },
|
||||
{ name: 'Namibia',
|
||||
dialCode: '264',
|
||||
code: 'na' },
|
||||
{ name: 'Nauru',
|
||||
dialCode: '674',
|
||||
code: 'nr' },
|
||||
{ name: 'Nepal',
|
||||
dialCode: '977',
|
||||
code: 'np' },
|
||||
{ name: 'Netherlands',
|
||||
dialCode: '31',
|
||||
code: 'nl' },
|
||||
{ name: 'Netherlands Antilles',
|
||||
dialCode: '599',
|
||||
code: 'an' },
|
||||
{ name: 'New Caledonia',
|
||||
dialCode: '687',
|
||||
code: 'nc' },
|
||||
{ name: 'New Zealand',
|
||||
dialCode: '64',
|
||||
code: 'nz' },
|
||||
{ name: 'Nicaragua',
|
||||
dialCode: '505',
|
||||
code: 'ni' },
|
||||
{ name: 'Niger',
|
||||
dialCode: '227',
|
||||
code: 'ne' },
|
||||
{ name: 'Nigeria',
|
||||
dialCode: '234',
|
||||
code: 'ng' },
|
||||
{ name: 'Niue',
|
||||
dialCode: '683',
|
||||
code: 'nu' },
|
||||
{ name: 'Norfolk Island',
|
||||
dialCode: '672',
|
||||
code: 'nf' },
|
||||
{ name: 'Northern Mariana Islands',
|
||||
dialCode: '1670',
|
||||
code: 'mp' },
|
||||
{ name: 'Norway',
|
||||
dialCode: '47',
|
||||
code: 'no' },
|
||||
{ name: 'Oman',
|
||||
dialCode: '968',
|
||||
code: 'om' },
|
||||
{ name: 'Pakistan',
|
||||
dialCode: '92',
|
||||
code: 'pk' },
|
||||
{ name: 'Palau',
|
||||
dialCode: '680',
|
||||
code: 'pw' },
|
||||
{ name: 'Palestinian Territory, Occupied',
|
||||
dialCode: '970',
|
||||
code: 'ps' },
|
||||
{ name: 'Panama',
|
||||
dialCode: '507',
|
||||
code: 'pa' },
|
||||
{ name: 'Papua New Guinea',
|
||||
dialCode: '675',
|
||||
code: 'pg' },
|
||||
{ name: 'Paraguay',
|
||||
dialCode: '595',
|
||||
code: 'py' },
|
||||
{ name: 'Peru',
|
||||
dialCode: '51',
|
||||
code: 'pe' },
|
||||
{ name: 'Philippines',
|
||||
dialCode: '63',
|
||||
code: 'ph' },
|
||||
{ name: 'Pitcairn',
|
||||
dialCode: '872',
|
||||
code: 'pn' },
|
||||
{ name: 'Poland',
|
||||
dialCode: '48',
|
||||
code: 'pl' },
|
||||
{ name: 'Portugal',
|
||||
dialCode: '351',
|
||||
code: 'pt' },
|
||||
{ name: 'Puerto Rico',
|
||||
dialCode: '1939',
|
||||
code: 'pr' },
|
||||
{ name: 'Qatar',
|
||||
dialCode: '974',
|
||||
code: 'qa' },
|
||||
{ name: 'Romania',
|
||||
dialCode: '40',
|
||||
code: 'ro' },
|
||||
{ name: 'Russia',
|
||||
dialCode: '7',
|
||||
code: 'ru' },
|
||||
{ name: 'Rwanda',
|
||||
dialCode: '250',
|
||||
code: 'rw' },
|
||||
{ name: 'Reunion',
|
||||
dialCode: '262',
|
||||
code: 're' },
|
||||
{ name: 'Saint Barthelemy',
|
||||
dialCode: '590',
|
||||
code: 'bl' },
|
||||
{
|
||||
name: 'Saint Helena, Ascension and Tristan Da Cunha',
|
||||
dialCode: '290',
|
||||
code: 'sh'
|
||||
},
|
||||
{ name: 'Saint Kitts and Nevis',
|
||||
dialCode: '1869',
|
||||
code: 'kn' },
|
||||
{ name: 'Saint Lucia',
|
||||
dialCode: '1758',
|
||||
code: 'lc' },
|
||||
{ name: 'Saint Martin',
|
||||
dialCode: '590',
|
||||
code: 'mf' },
|
||||
{ name: 'Saint Pierre and Miquelon',
|
||||
dialCode: '508',
|
||||
code: 'pm' },
|
||||
{ name: 'Saint Vincent and the Grenadines',
|
||||
dialCode: '1784',
|
||||
code: 'vc' },
|
||||
{ name: 'Samoa',
|
||||
dialCode: '685',
|
||||
code: 'ws' },
|
||||
{ name: 'San Marino',
|
||||
dialCode: '378',
|
||||
code: 'sm' },
|
||||
{ name: 'Sao Tome and Principe',
|
||||
dialCode: '239',
|
||||
code: 'st' },
|
||||
{ name: 'Saudi Arabia',
|
||||
dialCode: '966',
|
||||
code: 'sa' },
|
||||
{ name: 'Senegal',
|
||||
dialCode: '221',
|
||||
code: 'sn' },
|
||||
{ name: 'Serbia',
|
||||
dialCode: '381',
|
||||
code: 'rs' },
|
||||
{ name: 'Seychelles',
|
||||
dialCode: '248',
|
||||
code: 'sc' },
|
||||
{ name: 'Sierra Leone',
|
||||
dialCode: '232',
|
||||
code: 'sl' },
|
||||
{ name: 'Singapore',
|
||||
dialCode: '65',
|
||||
code: 'sg' },
|
||||
{ name: 'Slovakia',
|
||||
dialCode: '421',
|
||||
code: 'sk' },
|
||||
{ name: 'Slovenia',
|
||||
dialCode: '386',
|
||||
code: 'si' },
|
||||
{ name: 'Solomon Islands',
|
||||
dialCode: '677',
|
||||
code: 'sb' },
|
||||
{ name: 'Somalia',
|
||||
dialCode: '252',
|
||||
code: 'so' },
|
||||
{ name: 'South Africa',
|
||||
dialCode: '27',
|
||||
code: 'za' },
|
||||
{ name: 'South Sudan',
|
||||
dialCode: '211',
|
||||
code: 'ss' },
|
||||
{
|
||||
name: 'South Georgia and the South Sandwich Islands',
|
||||
dialCode: '500',
|
||||
code: 'gs'
|
||||
},
|
||||
{ name: 'Spain',
|
||||
dialCode: '34',
|
||||
code: 'es' },
|
||||
{ name: 'Sri Lanka',
|
||||
dialCode: '94',
|
||||
code: 'lk' },
|
||||
{ name: 'Sudan',
|
||||
dialCode: '249',
|
||||
code: 'sd' },
|
||||
{ name: 'Suriname',
|
||||
dialCode: '597',
|
||||
code: 'sr' },
|
||||
{ name: 'Svalbard and Jan Mayen',
|
||||
dialCode: '47',
|
||||
code: 'sj' },
|
||||
{ name: 'Swaziland',
|
||||
dialCode: '268',
|
||||
code: 'sz' },
|
||||
{ name: 'Sweden',
|
||||
dialCode: '46',
|
||||
code: 'se' },
|
||||
{ name: 'Switzerland',
|
||||
dialCode: '41',
|
||||
code: 'ch' },
|
||||
{ name: 'Syrian Arab Republic',
|
||||
dialCode: '963',
|
||||
code: 'sy' },
|
||||
{ name: 'Taiwan',
|
||||
dialCode: '886',
|
||||
code: 'tw' },
|
||||
{ name: 'Tajikistan',
|
||||
dialCode: '992',
|
||||
code: 'tj' },
|
||||
{
|
||||
name: 'Tanzania, United Republic of Tanzania',
|
||||
dialCode: '255',
|
||||
code: 'tz'
|
||||
},
|
||||
{ name: 'Thailand',
|
||||
dialCode: '66',
|
||||
code: 'th' },
|
||||
{ name: 'Timor-Leste',
|
||||
dialCode: '670',
|
||||
code: 'tl' },
|
||||
{ name: 'Togo',
|
||||
dialCode: '228',
|
||||
code: 'tg' },
|
||||
{ name: 'Tokelau',
|
||||
dialCode: '690',
|
||||
code: 'tk' },
|
||||
{ name: 'Tonga',
|
||||
dialCode: '676',
|
||||
code: 'to' },
|
||||
{ name: 'Trinidad and Tobago',
|
||||
dialCode: '1868',
|
||||
code: 'tt' },
|
||||
{ name: 'Tunisia',
|
||||
dialCode: '216',
|
||||
code: 'tn' },
|
||||
{ name: 'Turkey',
|
||||
dialCode: '90',
|
||||
code: 'tr' },
|
||||
{ name: 'Turkmenistan',
|
||||
dialCode: '993',
|
||||
code: 'tm' },
|
||||
{ name: 'Turks and Caicos Islands',
|
||||
dialCode: '1649',
|
||||
code: 'tc' },
|
||||
{ name: 'Tuvalu',
|
||||
dialCode: '688',
|
||||
code: 'tv' },
|
||||
{ name: 'Uganda',
|
||||
dialCode: '256',
|
||||
code: 'ug' },
|
||||
{ name: 'Ukraine',
|
||||
dialCode: '380',
|
||||
code: 'ua' },
|
||||
{ name: 'United Arab Emirates',
|
||||
dialCode: '971',
|
||||
code: 'ae' },
|
||||
{ name: 'United Kingdom',
|
||||
dialCode: '44',
|
||||
code: 'gb' },
|
||||
{ name: 'United States',
|
||||
dialCode: '1',
|
||||
code: 'us' },
|
||||
{ name: 'Uruguay',
|
||||
dialCode: '598',
|
||||
code: 'uy' },
|
||||
{ name: 'Uzbekistan',
|
||||
dialCode: '998',
|
||||
code: 'uz' },
|
||||
{ name: 'Vanuatu',
|
||||
dialCode: '678',
|
||||
code: 'vu' },
|
||||
{
|
||||
name: 'Venezuela, Bolivarian Republic of Venezuela',
|
||||
dialCode: '58',
|
||||
code: 've'
|
||||
},
|
||||
{ name: 'Vietnam',
|
||||
dialCode: '84',
|
||||
code: 'vn' },
|
||||
{ name: 'Virgin Islands, British',
|
||||
dialCode: '1284',
|
||||
code: 'vg' },
|
||||
{ name: 'Virgin Islands, U.S.',
|
||||
dialCode: '1340',
|
||||
code: 'vi' },
|
||||
{ name: 'Wallis and Futuna',
|
||||
dialCode: '681',
|
||||
code: 'wf' },
|
||||
{ name: 'Yemen',
|
||||
dialCode: '967',
|
||||
code: 'ye' },
|
||||
{ name: 'Zambia',
|
||||
dialCode: '260',
|
||||
code: 'zm' },
|
||||
{ name: 'Zimbabwe',
|
||||
dialCode: '263',
|
||||
code: 'zw' }
|
||||
];
|
||||
|
||||
const countriesByCodeMap = countries.reduce((result, country) => {
|
||||
result[country.dialCode] = country;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Map between country dial codes and country objects.
|
||||
*
|
||||
*/
|
||||
const codesByNumbersMap = countries.reduce((result, country) => {
|
||||
result[country.dialCode] = country.code;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Returns the corresponding country code from a phone number.
|
||||
*
|
||||
* @param {string} phoneNumber - The phone number.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getCountryCodeFromPhone(phoneNumber: string): string {
|
||||
const number = phoneNumber.replace(/[+.\s]/g, '');
|
||||
|
||||
|
||||
for (let i = 4; i > 0; i--) {
|
||||
const prefix = number.slice(0, i);
|
||||
|
||||
if (codesByNumbersMap[prefix]) {
|
||||
return codesByNumbersMap[prefix];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding country for a text starting with the dial code.
|
||||
*
|
||||
* @param {string} text - The text containing the dial code.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getCountryFromDialCodeText(text: string): Object {
|
||||
return (
|
||||
countriesByCodeMap[text.slice(0, 4)]
|
||||
|| countriesByCodeMap[text.slice(0, 3)]
|
||||
|| countriesByCodeMap[text.slice(0, 2)]
|
||||
|| countriesByCodeMap[text.slice(0, 1)]
|
||||
|| null
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue