|
|
|
|
@ -1,3 +1,5 @@ |
|
|
|
|
// @flow
|
|
|
|
|
|
|
|
|
|
import jwtDecode from 'jwt-decode'; |
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
@ -22,6 +24,8 @@ import { setCallOverlayVisible, setJWT } from './actions'; |
|
|
|
|
import { SET_JWT } from './actionTypes'; |
|
|
|
|
import { parseJWTFromURLParams } from './functions'; |
|
|
|
|
|
|
|
|
|
declare var APP: Object; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Middleware to parse token data upon setting a new room URL. |
|
|
|
|
* |
|
|
|
|
@ -80,8 +84,8 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { |
|
|
|
|
if (stateFeaturesJWT.callee) { |
|
|
|
|
const { conference, leaving, room } = state['features/base/conference']; |
|
|
|
|
|
|
|
|
|
// XXX The CallOverlay is to be displayed/visible as soon as
|
|
|
|
|
// possible including even before the conference is joined.
|
|
|
|
|
// XXX The CallOverlay is to be displayed/visible as soon as possible
|
|
|
|
|
// including even before the conference is joined.
|
|
|
|
|
if (room && (!conference || conference !== leaving)) { |
|
|
|
|
switch (action.type) { |
|
|
|
|
case CONFERENCE_FAILED: |
|
|
|
|
@ -100,10 +104,11 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: { |
|
|
|
|
// The CallOverlay it to no longer be displayed/visible as soon
|
|
|
|
|
// The CallOverlay is to no longer be displayed/visible as soon
|
|
|
|
|
// as another participant joins.
|
|
|
|
|
callOverlayVisible = getParticipantCount(state) === 1 |
|
|
|
|
&& Boolean(getLocalParticipant(state)); |
|
|
|
|
callOverlayVisible |
|
|
|
|
= getParticipantCount(state) === 1 |
|
|
|
|
&& Boolean(getLocalParticipant(state)); |
|
|
|
|
|
|
|
|
|
// However, the CallDialog is not to be displayed/visible again
|
|
|
|
|
// after all remote participants leave.
|
|
|
|
|
@ -122,53 +127,24 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Converts 'context.user' JWT token structure to the format compatible with the |
|
|
|
|
* corresponding fields overridden in base/participants. |
|
|
|
|
* |
|
|
|
|
* @param {Object} user - The 'jwt.context.user' structure parsed from the JWT |
|
|
|
|
* token. |
|
|
|
|
* @returns {({ |
|
|
|
|
* avatarURL: string?, |
|
|
|
|
* email: string?, |
|
|
|
|
* name: string? |
|
|
|
|
* })} |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
function _normalizeCallerFields(user) { |
|
|
|
|
const { avatar, avatarUrl, email, name } = user; |
|
|
|
|
const caller = { }; |
|
|
|
|
|
|
|
|
|
if (typeof (avatarUrl || avatar) === 'string') { |
|
|
|
|
caller.avatarURL = (avatarUrl || avatar).trim(); |
|
|
|
|
} |
|
|
|
|
if (typeof email === 'string') { |
|
|
|
|
caller.email = email.trim(); |
|
|
|
|
} |
|
|
|
|
if (typeof name === 'string') { |
|
|
|
|
caller.name = name.trim(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Object.keys(caller).length ? caller : undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Eventually overwrites 'avatarURL', 'email' and 'name' fields with the values |
|
|
|
|
* from JWT token for the local participant stored in the 'base/participants' |
|
|
|
|
* Redux store by dispatching the participant updated action. |
|
|
|
|
* Overwrites the properties {@code avatarURL}, {@code email}, and {@code name} |
|
|
|
|
* of the local participant stored in the redux state base/participants. |
|
|
|
|
* |
|
|
|
|
* @param {Store} store - The redux store. |
|
|
|
|
* @param {Object} caller - The "caller" structure parsed from 'context.user' |
|
|
|
|
* part of the JWT token and then normalized using |
|
|
|
|
* {@link _normalizeCallerFields}. |
|
|
|
|
* @returns {void} |
|
|
|
|
* @param {Object} localParticipant - The {@code Participant} structure to |
|
|
|
|
* overwrite the local participant stored in the redux store base/participants |
|
|
|
|
* with. |
|
|
|
|
* @private |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
function _overwriteLocalParticipant({ dispatch, getState }, caller) { |
|
|
|
|
const { avatarURL, email, name } = caller; |
|
|
|
|
const localParticipant = getLocalParticipant(getState()); |
|
|
|
|
function _overwriteLocalParticipant( |
|
|
|
|
{ dispatch, getState }, |
|
|
|
|
{ avatarURL, email, name }) { |
|
|
|
|
let localParticipant; |
|
|
|
|
|
|
|
|
|
if (localParticipant && (avatarURL || email || name)) { |
|
|
|
|
const newProperties = { id: localParticipant.id }; |
|
|
|
|
if ((avatarURL || email || name) |
|
|
|
|
&& (localParticipant = getLocalParticipant(getState))) { |
|
|
|
|
const newProperties: Object = { id: localParticipant.id }; |
|
|
|
|
|
|
|
|
|
if (avatarURL) { |
|
|
|
|
newProperties.avatarURL = avatarURL; |
|
|
|
|
@ -183,41 +159,6 @@ function _overwriteLocalParticipant({ dispatch, getState }, caller) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Will reset the values overridden by {@link _overwriteLocalParticipant} |
|
|
|
|
* by either clearing them or setting to default values. Only the values that |
|
|
|
|
* have not changed since the override happened will be restored. |
|
|
|
|
* |
|
|
|
|
* NOTE Once there is the possibility to edit and save participant properties, |
|
|
|
|
* this method should restore values from the storage instead. |
|
|
|
|
* |
|
|
|
|
* @param {Store} store - The Redux store. |
|
|
|
|
* @param {Object} caller - The 'caller' part of the JWT Redux state which tells |
|
|
|
|
* which local participant's fields's been overridden when the JWT token was |
|
|
|
|
* set. |
|
|
|
|
* @returns {void} |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
function _resetLocalParticipantOverrides({ dispatch, getState }, caller) { |
|
|
|
|
const { avatarURL, name, email } = caller; |
|
|
|
|
const localParticipant = getLocalParticipant(getState()); |
|
|
|
|
|
|
|
|
|
if (localParticipant && (avatarURL || name || email)) { |
|
|
|
|
const newProperties = { id: localParticipant.id }; |
|
|
|
|
|
|
|
|
|
if (avatarURL === localParticipant.avatarURL) { |
|
|
|
|
newProperties.avatarURL = undefined; |
|
|
|
|
} |
|
|
|
|
if (name === localParticipant.name) { |
|
|
|
|
newProperties.name = LOCAL_PARTICIPANT_DEFAULT_NAME; |
|
|
|
|
} |
|
|
|
|
if (email === localParticipant.email) { |
|
|
|
|
newProperties.email = undefined; |
|
|
|
|
} |
|
|
|
|
dispatch(participantUpdated(newProperties)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Notifies the feature jwt that the action {@link SET_CONFIG} or |
|
|
|
|
* {@link SET_LOCATION_URL} is being dispatched within a specific redux |
|
|
|
|
@ -266,41 +207,108 @@ function _setJWT(store, next, action) { |
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
|
const { jwt, type, ...actionPayload } = action; |
|
|
|
|
|
|
|
|
|
if (jwt && !Object.keys(actionPayload).length) { |
|
|
|
|
const { |
|
|
|
|
enableUserRolesBasedOnToken |
|
|
|
|
} = store.getState()['features/base/config']; |
|
|
|
|
if (!Object.keys(actionPayload).length) { |
|
|
|
|
if (jwt) { |
|
|
|
|
const { |
|
|
|
|
enableUserRolesBasedOnToken |
|
|
|
|
} = store.getState()['features/base/config']; |
|
|
|
|
|
|
|
|
|
action.isGuest = !enableUserRolesBasedOnToken; |
|
|
|
|
|
|
|
|
|
action.isGuest = !enableUserRolesBasedOnToken; |
|
|
|
|
const jwtPayload = jwtDecode(jwt); |
|
|
|
|
|
|
|
|
|
const jwtPayload = jwtDecode(jwt); |
|
|
|
|
if (jwtPayload) { |
|
|
|
|
const { context, iss } = jwtPayload; |
|
|
|
|
|
|
|
|
|
if (jwtPayload) { |
|
|
|
|
const { context, iss } = jwtPayload; |
|
|
|
|
action.jwt = jwt; |
|
|
|
|
action.issuer = iss; |
|
|
|
|
if (context) { |
|
|
|
|
const user = _user2participant(context.user); |
|
|
|
|
|
|
|
|
|
action.jwt = jwt; |
|
|
|
|
action.issuer = iss; |
|
|
|
|
if (context) { |
|
|
|
|
action.callee = context.callee; |
|
|
|
|
action.caller = _normalizeCallerFields(context.user); |
|
|
|
|
action.group = context.group; |
|
|
|
|
action.server = context.server; |
|
|
|
|
action.callee = context.callee; |
|
|
|
|
action.group = context.group; |
|
|
|
|
action.server = context.server; |
|
|
|
|
action.user = user; |
|
|
|
|
|
|
|
|
|
if (action.caller) { |
|
|
|
|
_overwriteLocalParticipant(store, action.caller); |
|
|
|
|
user && _overwriteLocalParticipant(store, user); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (!jwt && !Object.keys(actionPayload).length) { |
|
|
|
|
const jwtState = store.getState()['features/base/jwt']; |
|
|
|
|
|
|
|
|
|
// The logic of restoring JWT overrides make sense only on mobile. On
|
|
|
|
|
// web it should eventually be restored from storage, but there's no
|
|
|
|
|
// such use case yet.
|
|
|
|
|
if (jwtState.caller && typeof APP === 'undefined') { |
|
|
|
|
_resetLocalParticipantOverrides(store, jwtState.caller); |
|
|
|
|
} else if (typeof APP === 'undefined') { |
|
|
|
|
// The logic of restoring JWT overrides make sense only on mobile.
|
|
|
|
|
// On Web it should eventually be restored from storage, but there's
|
|
|
|
|
// no such use case yet.
|
|
|
|
|
|
|
|
|
|
const { user } = store.getState()['features/base/jwt']; |
|
|
|
|
|
|
|
|
|
user && _undoOverwriteLocalParticipant(store, user); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return _maybeSetCallOverlayVisible(store, next, action); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Undoes/resets the values overwritten by {@link _overwriteLocalParticipant} |
|
|
|
|
* by either clearing them or setting to default values. Only the values that |
|
|
|
|
* have not changed since the overwrite happened will be restored. |
|
|
|
|
* |
|
|
|
|
* NOTE Once it is possible to edit and save participant properties, this |
|
|
|
|
* function should restore values from the storage instead. |
|
|
|
|
* |
|
|
|
|
* @param {Store} store - The redux store. |
|
|
|
|
* @param {Object} localParticipant - The {@code Participant} structure used |
|
|
|
|
* previously to {@link _overwriteLocalParticipant}. |
|
|
|
|
* @private |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
function _undoOverwriteLocalParticipant( |
|
|
|
|
{ dispatch, getState }, |
|
|
|
|
{ avatarURL, name, email }) { |
|
|
|
|
let localParticipant; |
|
|
|
|
|
|
|
|
|
if ((avatarURL || name || email) |
|
|
|
|
&& (localParticipant = getLocalParticipant(getState))) { |
|
|
|
|
const newProperties: Object = { id: localParticipant.id }; |
|
|
|
|
|
|
|
|
|
if (avatarURL === localParticipant.avatarURL) { |
|
|
|
|
newProperties.avatarURL = undefined; |
|
|
|
|
} |
|
|
|
|
if (email === localParticipant.email) { |
|
|
|
|
newProperties.email = undefined; |
|
|
|
|
} |
|
|
|
|
if (name === localParticipant.name) { |
|
|
|
|
newProperties.name = LOCAL_PARTICIPANT_DEFAULT_NAME; |
|
|
|
|
} |
|
|
|
|
dispatch(participantUpdated(newProperties)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Converts the JWT {@code context.user} structure to the {@code Participant} |
|
|
|
|
* structure stored in the redux state base/participants. |
|
|
|
|
* |
|
|
|
|
* @param {Object} user - The JWT {@code context.user} structure to convert. |
|
|
|
|
* @private |
|
|
|
|
* @returns {{ |
|
|
|
|
* avatarURL: ?string, |
|
|
|
|
* email: ?string, |
|
|
|
|
* name: ?string |
|
|
|
|
* }} |
|
|
|
|
*/ |
|
|
|
|
function _user2participant({ avatar, avatarUrl, email, name }) { |
|
|
|
|
const participant = {}; |
|
|
|
|
|
|
|
|
|
if (typeof avatarUrl === 'string') { |
|
|
|
|
participant.avatarURL = avatarUrl.trim(); |
|
|
|
|
} else if (typeof avatar === 'string') { |
|
|
|
|
participant.avatarURL = avatar.trim(); |
|
|
|
|
} |
|
|
|
|
if (typeof email === 'string') { |
|
|
|
|
participant.email = email.trim(); |
|
|
|
|
} |
|
|
|
|
if (typeof name === 'string') { |
|
|
|
|
participant.name = name.trim(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Object.keys(participant).length ? participant : undefined; |
|
|
|
|
} |
|
|
|
|
|