Move display name handling into redux

This commit is contained in:
Bettenbuk Zoltan 2019-01-13 20:33:28 +01:00 committed by Saúl Ibarra Corretgé
parent 8c9ba325ca
commit 82f714b608
9 changed files with 167 additions and 133 deletions

View File

@ -2660,7 +2660,6 @@ export default {
}); });
if (room) { if (room) {
room.setDisplayName(formattedNickname);
APP.UI.changeDisplayName(id, formattedNickname); APP.UI.changeDisplayName(id, formattedNickname);
} }
}, },

View File

@ -15,6 +15,7 @@ import {
getLocalParticipant, getLocalParticipant,
getParticipantById, getParticipantById,
getPinnedParticipant, getPinnedParticipant,
PARTICIPANT_UPDATED,
PIN_PARTICIPANT PIN_PARTICIPANT
} from '../participants'; } from '../participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../redux'; import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
@ -80,6 +81,9 @@ MiddlewareRegistry.register(store => next => action => {
case DATA_CHANNEL_OPENED: case DATA_CHANNEL_OPENED:
return _syncReceiveVideoQuality(store, next, action); return _syncReceiveVideoQuality(store, next, action);
case PARTICIPANT_UPDATED:
return _updateLocalParticipantInConference(store, next, action);
case PIN_PARTICIPANT: case PIN_PARTICIPANT:
return _pinParticipant(store, next, action); return _pinParticipant(store, next, action);
@ -651,3 +655,27 @@ function _trackAddedOrRemoved(store, next, action) {
return next(action); return next(action);
} }
/**
* Updates the conference object when the local participant is updated.
*
* @param {Store} store - The redux store in which the specified {@code action}
* is being dispatched.
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
* specified {@code action} to the specified {@code store}.
* @param {Action} action - The redux action which is being dispatched in the
* specified {@code store}.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _updateLocalParticipantInConference({ getState }, next, action) {
const { conference } = getState()['features/base/conference'];
const { participant } = action;
const result = next(action);
if (conference && participant.local) {
conference.setDisplayName(participant.name);
}
return result;
}

View File

@ -11,7 +11,6 @@ import {
HIDDEN_PARTICIPANT_LEFT, HIDDEN_PARTICIPANT_LEFT,
KICK_PARTICIPANT, KICK_PARTICIPANT,
MUTE_REMOTE_PARTICIPANT, MUTE_REMOTE_PARTICIPANT,
PARTICIPANT_DISPLAY_NAME_CHANGED,
PARTICIPANT_ID_CHANGED, PARTICIPANT_ID_CHANGED,
PARTICIPANT_JOINED, PARTICIPANT_JOINED,
PARTICIPANT_LEFT, PARTICIPANT_LEFT,
@ -208,29 +207,6 @@ export function participantConnectionStatusChanged(id, connectionStatus) {
}; };
} }
/**
* Action to signal that a participant's display name has changed.
*
* @param {string} id - The id of the participant being changed.
* @param {string} displayName - The new display name.
* @returns {{
* type: PARTICIPANT_DISPLAY_NAME_CHANGED,
* id: string,
* name: string
* }}
*/
export function participantDisplayNameChanged(id, displayName = '') {
// FIXME Do not use this action over participantUpdated. This action exists
// as a a bridge for local name updates. Once other components responsible
// for updating the local user's display name are in react/redux, this
// action should be replaceable with the participantUpdated action.
return {
type: PARTICIPANT_DISPLAY_NAME_CHANGED,
id,
name: displayName.substr(0, MAX_DISPLAY_NAME_LENGTH)
};
}
/** /**
* Action to signal that a participant has joined. * Action to signal that a participant has joined.
* *
@ -393,6 +369,10 @@ export function participantRoleChanged(id, role) {
* }} * }}
*/ */
export function participantUpdated(participant = {}) { export function participantUpdated(participant = {}) {
if (participant.name) {
participant.name = participant.name.substr(0, MAX_DISPLAY_NAME_LENGTH);
}
return { return {
type: PARTICIPANT_UPDATED, type: PARTICIPANT_UPDATED,
participant participant

View File

@ -5,21 +5,13 @@ import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { import { updateSettings } from '../../base/settings';
getLocalParticipant,
participantDisplayNameChanged
} from '../../base/participants';
/** /**
* The type of the React {@code Component} props of {@DisplayNameForm}. * The type of the React {@code Component} props of {@DisplayNameForm}.
*/ */
type Props = { type Props = {
/**
* The ID of the local participant.
*/
_localParticipantId: string,
/** /**
* Invoked to set the local participant display name. * Invoked to set the local participant display name.
*/ */
@ -117,26 +109,11 @@ class DisplayNameForm extends Component<Props, State> {
_onSubmit(event: Object) { _onSubmit(event: Object) {
event.preventDefault(); event.preventDefault();
this.props.dispatch(participantDisplayNameChanged( // Store display name in settings
this.props._localParticipantId, this.props.dispatch(updateSettings({
this.state.displayName)); displayName: this.state.displayName
}));
} }
} }
/** export default translate(connect()(DisplayNameForm));
* Maps (parts of) the Redux state to the associated props for the
* {@code DisplayNameForm} component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _localParticipantId: string
* }}
*/
function _mapStateToProps(state) {
return {
_localParticipantId: getLocalParticipant(state).id
};
}
export default translate(connect(_mapStateToProps)(DisplayNameForm));

View File

@ -1,3 +1,5 @@
// @flow
import { openDialog } from '../../features/base/dialog'; import { openDialog } from '../../features/base/dialog';
import { DisplayNamePrompt } from './components'; import { DisplayNamePrompt } from './components';
@ -5,8 +7,12 @@ import { DisplayNamePrompt } from './components';
/** /**
* Signals to open a dialog with the {@code DisplayNamePrompt} component. * Signals to open a dialog with the {@code DisplayNamePrompt} component.
* *
* @param {?Function} onPostSubmit - The function to invoke after a successful
* submit of the dialog.
* @returns {Object} * @returns {Object}
*/ */
export function openDisplayNamePrompt() { export function openDisplayNamePrompt(onPostSubmit: ?Function = undefined) {
return openDialog(DisplayNamePrompt); return openDialog(DisplayNamePrompt, {
onPostSubmit
});
} }

View File

@ -0,0 +1,74 @@
// @flow
import { Component } from 'react';
import { updateSettings } from '../../base/settings';
/**
* The type of the React {@code Component} props of
* {@link AbstractDisplayNamePrompt}.
*/
export type Props = {
/**
* Invoked to update the local participant's display name.
*/
dispatch: Dispatch<*>,
/**
* Function to be invoked after a successful display name change.
*/
onPostSubmit: ?Function,
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/**
* Implements an abstract class for {@code DisplayNamePrompt}.
*/
export default class AbstractDisplayNamePrompt<S: *>
extends Component<Props, S> {
/**
* Instantiates a new component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onSetDisplayName = this._onSetDisplayName.bind(this);
}
_onSetDisplayName: string => boolean;
/**
* Dispatches an action to update the local participant's display name. A
* name must be entered for the action to dispatch.
*
* It returns a boolean to comply the Dialog behaviour:
* {@code true} - the dialog should be closed.
* {@code false} - the dialog should be left open.
*
* @param {string} displayName - The display name to save.
* @returns {boolean}
*/
_onSetDisplayName(displayName) {
if (!displayName || !displayName.trim()) {
return false;
}
const { dispatch, onPostSubmit } = this.props;
// Store display name in settings
dispatch(updateSettings({
displayName
}));
onPostSubmit && onPostSubmit();
return true;
}
}

View File

@ -6,13 +6,21 @@ import { connect } from 'react-redux';
import { appendSuffix } from '../functions'; import { appendSuffix } from '../functions';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { participantDisplayNameChanged } from '../../base/participants'; import {
getParticipantDisplayName
} from '../../base/participants';
import { updateSettings } from '../../base/settings';
/** /**
* The type of the React {@code Component} props of {@link DisplayName}. * The type of the React {@code Component} props of {@link DisplayName}.
*/ */
type Props = { type Props = {
/**
* The participant's current display name.
*/
_displayName: string,
/** /**
* Whether or not the display name should be editable on click. * Whether or not the display name should be editable on click.
*/ */
@ -23,11 +31,6 @@ type Props = {
*/ */
dispatch: Dispatch<*>, dispatch: Dispatch<*>,
/**
* The participant's current display name.
*/
displayName: string,
/** /**
* A string to append to the displayName, if provided. * A string to append to the displayName, if provided.
*/ */
@ -130,8 +133,8 @@ class DisplayName extends Component<Props, State> {
*/ */
render() { render() {
const { const {
_displayName,
allowEditing, allowEditing,
displayName,
displayNameSuffix, displayNameSuffix,
elementID, elementID,
t t
@ -159,7 +162,7 @@ class DisplayName extends Component<Props, State> {
className = 'displayname' className = 'displayname'
id = { elementID } id = { elementID }
onClick = { this._onStartEditing }> onClick = { this._onStartEditing }>
{ appendSuffix(displayName, displayNameSuffix) } { appendSuffix(_displayName, displayNameSuffix) }
</span> </span>
); );
} }
@ -208,7 +211,7 @@ class DisplayName extends Component<Props, State> {
if (this.props.allowEditing) { if (this.props.allowEditing) {
this.setState({ this.setState({
isEditing: true, isEditing: true,
editDisplayNameValue: this.props.displayName || '' editDisplayNameValue: this.props._displayName || ''
}); });
} }
} }
@ -226,10 +229,12 @@ class DisplayName extends Component<Props, State> {
*/ */
_onSubmit() { _onSubmit() {
const { editDisplayNameValue } = this.state; const { editDisplayNameValue } = this.state;
const { dispatch, participantID } = this.props; const { dispatch } = this.props;
dispatch(participantDisplayNameChanged( // Store display name in settings
participantID, editDisplayNameValue)); dispatch(updateSettings({
displayName: editDisplayNameValue
}));
this.setState({ this.setState({
isEditing: false, isEditing: false,
@ -255,4 +260,23 @@ class DisplayName extends Component<Props, State> {
} }
} }
export default translate(connect()(DisplayName)); /**
* Maps (parts of) the redux state to the props of this component.
*
* @param {Object} state - The redux store/state.
* @param {Props} ownProps - The own props of the component.
* @private
* @returns {{
* _displayName: string
* }}
*/
function _mapStateToProps(state, ownProps) {
const { participantID } = ownProps;
return {
_displayName: getParticipantDisplayName(
state, participantID)
};
}
export default translate(connect(_mapStateToProps)(DisplayName));

View File

@ -1,37 +1,15 @@
/* @flow */ /* @flow */
import React, { Component } from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { FieldTextStateless as TextField } from '@atlaskit/field-text'; import { FieldTextStateless as TextField } from '@atlaskit/field-text';
import { Dialog } from '../../base/dialog'; import { Dialog } from '../../base/dialog';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import {
getLocalParticipant,
participantDisplayNameChanged
} from '../../base/participants';
/** import AbstractDisplayNamePrompt, {
* The type of the React {@code Component} props of {@link DisplayNamePrompt}. type Props
*/ } from './AbstractDisplayNamePrompt';
type Props = {
/**
* The current ID for the local participant. Used for setting the display
* name on the associated participant.
*/
_localParticipantID: string,
/**
* Invoked to update the local participant's display name.
*/
dispatch: Dispatch<*>,
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/** /**
* The type of the React {@code Component} props of {@link DisplayNamePrompt}. * The type of the React {@code Component} props of {@link DisplayNamePrompt}.
@ -50,7 +28,7 @@ type State = {
* *
* @extends Component * @extends Component
*/ */
class DisplayNamePrompt extends Component<Props, State> { class DisplayNamePrompt extends AbstractDisplayNamePrompt<State> {
/** /**
* Initializes a new {@code DisplayNamePrompt} instance. * Initializes a new {@code DisplayNamePrompt} instance.
* *
@ -110,6 +88,8 @@ class DisplayNamePrompt extends Component<Props, State> {
}); });
} }
_onSetDisplayName: string => boolean;
_onSubmit: () => boolean; _onSubmit: () => boolean;
/** /**
@ -120,42 +100,8 @@ class DisplayNamePrompt extends Component<Props, State> {
* @returns {boolean} * @returns {boolean}
*/ */
_onSubmit() { _onSubmit() {
const { displayName } = this.state; return this._onSetDisplayName(this.state.displayName);
if (!displayName.trim()) {
return false;
}
const { dispatch, _localParticipantID } = this.props;
dispatch(
participantDisplayNameChanged(_localParticipantID, displayName));
return true;
} }
} }
/** export default translate(connect()(DisplayNamePrompt));
* Maps (parts of) the Redux state to the associated {@code DisplayNamePrompt}'s
* props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _localParticipantID: string
* }}
*/
function _mapStateToProps(state) {
const { id } = getLocalParticipant(state);
return {
/**
* The current ID for the local participant.
*
* @type {string}
*/
_localParticipantID: id
};
}
export default translate(connect(_mapStateToProps)(DisplayNamePrompt));

View File

@ -7,5 +7,5 @@
*/ */
export function appendSuffix(displayName, suffix) { export function appendSuffix(displayName, suffix) {
return `${displayName || suffix || ''}${ return `${displayName || suffix || ''}${
displayName && suffix ? ` (${suffix})` : ''}`; displayName && suffix && displayName !== suffix ? ` (${suffix})` : ''}`;
} }