Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
fb0269f6f9
21 changed files with 205 additions and 94 deletions
|
@ -65,6 +65,7 @@
|
||||||
"file-saver": "^1.3.3",
|
"file-saver": "^1.3.3",
|
||||||
"filesize": "3.5.6",
|
"filesize": "3.5.6",
|
||||||
"flux": "2.1.1",
|
"flux": "2.1.1",
|
||||||
|
"focus-trap-react": "^3.0.5",
|
||||||
"fuse.js": "^2.2.0",
|
"fuse.js": "^2.2.0",
|
||||||
"glob": "^5.0.14",
|
"glob": "^5.0.14",
|
||||||
"highlight.js": "^8.9.1",
|
"highlight.js": "^8.9.1",
|
||||||
|
|
13
src/Modal.js
13
src/Modal.js
|
@ -22,6 +22,7 @@ const ReactDOM = require('react-dom');
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Analytics from './Analytics';
|
import Analytics from './Analytics';
|
||||||
import sdk from './index';
|
import sdk from './index';
|
||||||
|
import dis from './dispatcher';
|
||||||
|
|
||||||
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
|
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
|
||||||
|
|
||||||
|
@ -188,10 +189,22 @@ class ModalManager {
|
||||||
|
|
||||||
_reRender() {
|
_reRender() {
|
||||||
if (this._modals.length == 0) {
|
if (this._modals.length == 0) {
|
||||||
|
// If there is no modal to render, make all of Riot available
|
||||||
|
// to screen reader users again
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'aria_unhide_main_app',
|
||||||
|
});
|
||||||
ReactDOM.unmountComponentAtNode(this.getOrCreateContainer());
|
ReactDOM.unmountComponentAtNode(this.getOrCreateContainer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide the content outside the modal to screen reader users
|
||||||
|
// so they won't be able to navigate into it and act on it using
|
||||||
|
// screen reader specific features
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'aria_hide_main_app',
|
||||||
|
});
|
||||||
|
|
||||||
const modal = this._modals[0];
|
const modal = this._modals[0];
|
||||||
const dialog = (
|
const dialog = (
|
||||||
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '')}>
|
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '')}>
|
||||||
|
|
|
@ -374,7 +374,7 @@ const LoggedInView = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mx_MatrixChat_wrapper'>
|
<div className='mx_MatrixChat_wrapper' aria-hidden={this.props.hideToSRUsers}>
|
||||||
{ topBar }
|
{ topBar }
|
||||||
<DragDropContext onDragEnd={this._onDragEnd}>
|
<DragDropContext onDragEnd={this._onDragEnd}>
|
||||||
<div className={bodyClasses}>
|
<div className={bodyClasses}>
|
||||||
|
|
|
@ -171,6 +171,10 @@ export default React.createClass({
|
||||||
register_hs_url: null,
|
register_hs_url: null,
|
||||||
register_is_url: null,
|
register_is_url: null,
|
||||||
register_id_sid: null,
|
register_id_sid: null,
|
||||||
|
|
||||||
|
// When showing Modal dialogs we need to set aria-hidden on the root app element
|
||||||
|
// and disable it when there are no dialogs
|
||||||
|
hideToSRUsers: false,
|
||||||
};
|
};
|
||||||
return s;
|
return s;
|
||||||
},
|
},
|
||||||
|
@ -608,6 +612,16 @@ export default React.createClass({
|
||||||
case 'send_event':
|
case 'send_event':
|
||||||
this.onSendEvent(payload.room_id, payload.event);
|
this.onSendEvent(payload.room_id, payload.event);
|
||||||
break;
|
break;
|
||||||
|
case 'aria_hide_main_app':
|
||||||
|
this.setState({
|
||||||
|
hideToSRUsers: true,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'aria_unhide_main_app':
|
||||||
|
this.setState({
|
||||||
|
hideToSRUsers: false,
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import FocusTrap from 'focus-trap-react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
@ -37,9 +38,6 @@ export default React.createClass({
|
||||||
// onFinished callback to call when Escape is pressed
|
// onFinished callback to call when Escape is pressed
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// callback to call when Enter is pressed
|
|
||||||
onEnterPressed: PropTypes.func,
|
|
||||||
|
|
||||||
// called when a key is pressed
|
// called when a key is pressed
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown: PropTypes.func,
|
||||||
|
|
||||||
|
@ -52,6 +50,10 @@ export default React.createClass({
|
||||||
|
|
||||||
// children should be the content of the dialog
|
// children should be the content of the dialog
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
|
||||||
|
// Id of content element
|
||||||
|
// If provided, this is used to add a aria-describedby attribute
|
||||||
|
contentId: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
childContextTypes: {
|
childContextTypes: {
|
||||||
|
@ -76,12 +78,6 @@ export default React.createClass({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
} else if (e.keyCode === KeyCode.ENTER) {
|
|
||||||
if (this.props.onEnterPressed) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
this.props.onEnterPressed(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -93,17 +89,28 @@ export default React.createClass({
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
<FocusTrap onKeyDown={this._onKeyDown}
|
||||||
|
className={this.props.className}
|
||||||
|
role="dialog"
|
||||||
|
aria-labelledby='mx_BaseDialog_title'
|
||||||
|
// This should point to a node describing the dialog.
|
||||||
|
// If we were about to completelly follow this recommendation we'd need to
|
||||||
|
// make all the components relying on BaseDialog to be aware of it.
|
||||||
|
// So instead we will use the whole content as the description.
|
||||||
|
// Description comes first and if the content contains more text,
|
||||||
|
// AT users can skip its presentation.
|
||||||
|
aria-describedby={this.props.contentId}
|
||||||
|
>
|
||||||
<AccessibleButton onClick={this._onCancelClick}
|
<AccessibleButton onClick={this._onCancelClick}
|
||||||
className="mx_Dialog_cancelButton"
|
className="mx_Dialog_cancelButton"
|
||||||
>
|
>
|
||||||
<TintableSvg src="img/icons-close-button.svg" width="35" height="35" />
|
<TintableSvg src="img/icons-close-button.svg" width="35" height="35" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
<div className={'mx_Dialog_title ' + this.props.titleClass}>
|
<div className={'mx_Dialog_title ' + this.props.titleClass} id='mx_BaseDialog_title'>
|
||||||
{ this.props.title }
|
{ this.props.title }
|
||||||
</div>
|
</div>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</div>
|
</FocusTrap>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -129,7 +129,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className={labelClasses}><i>{ _t("Start new chat") }</i></div>
|
<div className={labelClasses}><i>{ _t("Start new chat") }</i></div>
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
content = <div className="mx_Dialog_content">
|
content = <div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
{ _t('You already have existing direct chats with this user:') }
|
{ _t('You already have existing direct chats with this user:') }
|
||||||
<div className="mx_ChatCreateOrReuseDialog_tiles">
|
<div className="mx_ChatCreateOrReuseDialog_tiles">
|
||||||
{ this.state.tiles }
|
{ this.state.tiles }
|
||||||
|
@ -147,7 +147,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
if (this.state.busyProfile) {
|
if (this.state.busyProfile) {
|
||||||
profile = <Spinner />;
|
profile = <Spinner />;
|
||||||
} else if (this.state.profileError) {
|
} else if (this.state.profileError) {
|
||||||
profile = <div className="error">
|
profile = <div className="error" role="alert">
|
||||||
Unable to load profile information for { this.props.userId }
|
Unable to load profile information for { this.props.userId }
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,14 +163,14 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
content = <div>
|
content = <div>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
<p>
|
<p>
|
||||||
{ _t('Click on the button below to start chatting!') }
|
{ _t('Click on the button below to start chatting!') }
|
||||||
</p>
|
</p>
|
||||||
{ profile }
|
{ profile }
|
||||||
</div>
|
</div>
|
||||||
<DialogButtons primaryButton={_t('Start Chatting')}
|
<DialogButtons primaryButton={_t('Start Chatting')}
|
||||||
onPrimaryButtonClick={this.props.onNewDMClick} />
|
onPrimaryButtonClick={this.props.onNewDMClick} focus="true" />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +179,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
<BaseDialog className='mx_ChatCreateOrReuseDialog'
|
<BaseDialog className='mx_ChatCreateOrReuseDialog'
|
||||||
onFinished={this.props.onFinished.bind(false)}
|
onFinished={this.props.onFinished.bind(false)}
|
||||||
title={title}
|
title={title}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
{ content }
|
{ content }
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
|
@ -114,10 +114,10 @@ export default React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ConfirmUserActionDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_ConfirmUserActionDialog" onFinished={this.props.onFinished}
|
||||||
onEnterPressed={this.onOk}
|
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div id="mx_Dialog_content" className="mx_Dialog_content">
|
||||||
<div className="mx_ConfirmUserActionDialog_avatar">
|
<div className="mx_ConfirmUserActionDialog_avatar">
|
||||||
{ avatar }
|
{ avatar }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -112,7 +112,7 @@ export default React.createClass({
|
||||||
// XXX: We should catch errcodes and give sensible i18ned messages for them,
|
// XXX: We should catch errcodes and give sensible i18ned messages for them,
|
||||||
// rather than displaying what the server gives us, but synapse doesn't give
|
// rather than displaying what the server gives us, but synapse doesn't give
|
||||||
// any yet.
|
// any yet.
|
||||||
createErrorNode = <div className="error">
|
createErrorNode = <div className="error" role="alert">
|
||||||
<div>{ _t('Something went wrong whilst creating your community') }</div>
|
<div>{ _t('Something went wrong whilst creating your community') }</div>
|
||||||
<div>{ this.state.createError.message }</div>
|
<div>{ this.state.createError.message }</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -120,7 +120,6 @@ export default React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_CreateGroupDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_CreateGroupDialog" onFinished={this.props.onFinished}
|
||||||
onEnterPressed={this._onFormSubmit}
|
|
||||||
title={_t('Create Community')}
|
title={_t('Create Community')}
|
||||||
>
|
>
|
||||||
<form onSubmit={this._onFormSubmit}>
|
<form onSubmit={this._onFormSubmit}>
|
||||||
|
|
|
@ -45,9 +45,9 @@ export default React.createClass({
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
|
||||||
onEnterPressed={this.onOk}
|
|
||||||
title={_t('Create Room')}
|
title={_t('Create Room')}
|
||||||
>
|
>
|
||||||
|
<form onSubmit={this.onOk}>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<div className="mx_CreateRoomDialog_label">
|
<div className="mx_CreateRoomDialog_label">
|
||||||
<label htmlFor="textinput"> { _t('Room name (optional)') } </label>
|
<label htmlFor="textinput"> { _t('Room name (optional)') } </label>
|
||||||
|
@ -69,6 +69,7 @@ export default React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
<DialogButtons primaryButton={_t('Create Room')}
|
<DialogButtons primaryButton={_t('Create Room')}
|
||||||
onPrimaryButtonClick={this.onOk}
|
onPrimaryButtonClick={this.onOk}
|
||||||
onCancel={this.onCancel} />
|
onCancel={this.onCancel} />
|
||||||
|
|
|
@ -52,22 +52,18 @@ export default React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
if (this.props.focus) {
|
|
||||||
this.refs.button.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
||||||
title={this.props.title || _t('Error')}>
|
title={this.props.title || _t('Error')}
|
||||||
<div className="mx_Dialog_content">
|
contentId='mx_Dialog_content'
|
||||||
|
>
|
||||||
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
{ this.props.description || _t('An error has occurred.') }
|
{ this.props.description || _t('An error has occurred.') }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
<button ref="button" className="mx_Dialog_primary" onClick={this.props.onFinished}>
|
<button className="mx_Dialog_primary" onClick={this.props.onFinished} autoFocus={this.props.focus}>
|
||||||
{ this.props.button || _t('OK') }
|
{ this.props.button || _t('OK') }
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -73,11 +73,12 @@ export default React.createClass({
|
||||||
let content;
|
let content;
|
||||||
if (this.state.authError) {
|
if (this.state.authError) {
|
||||||
content = (
|
content = (
|
||||||
<div>
|
<div id='mx_Dialog_content'>
|
||||||
<div>{ this.state.authError.message || this.state.authError.toString() }</div>
|
<div role="alert">{ this.state.authError.message || this.state.authError.toString() }</div>
|
||||||
<br />
|
<br />
|
||||||
<AccessibleButton onClick={this._onDismissClick}
|
<AccessibleButton onClick={this._onDismissClick}
|
||||||
className="mx_UserSettings_button"
|
className="mx_UserSettings_button"
|
||||||
|
autoFocus="true"
|
||||||
>
|
>
|
||||||
{ _t("Dismiss") }
|
{ _t("Dismiss") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -85,7 +86,7 @@ export default React.createClass({
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<div>
|
<div id='mx_Dialog_content'>
|
||||||
<InteractiveAuth ref={this._collectInteractiveAuth}
|
<InteractiveAuth ref={this._collectInteractiveAuth}
|
||||||
matrixClient={this.props.matrixClient}
|
matrixClient={this.props.matrixClient}
|
||||||
authData={this.props.authData}
|
authData={this.props.authData}
|
||||||
|
@ -100,6 +101,7 @@ export default React.createClass({
|
||||||
<BaseDialog className="mx_InteractiveAuthDialog"
|
<BaseDialog className="mx_InteractiveAuthDialog"
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
|
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
{ content }
|
{ content }
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
|
@ -126,11 +126,11 @@ export default React.createClass({
|
||||||
text = _t(text, {displayName: displayName});
|
text = _t(text, {displayName: displayName});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div id='mx_Dialog_content'>
|
||||||
<p>{ text }</p>
|
<p>{ text }</p>
|
||||||
|
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
<button onClick={this._onVerifyClicked}>
|
<button onClick={this._onVerifyClicked} autoFocus="true">
|
||||||
{ _t('Start verification') }
|
{ _t('Start verification') }
|
||||||
</button>
|
</button>
|
||||||
<button onClick={this._onShareClicked}>
|
<button onClick={this._onShareClicked}>
|
||||||
|
@ -154,7 +154,7 @@ export default React.createClass({
|
||||||
content = this._renderContent();
|
content = this._renderContent();
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<div>
|
<div id='mx_Dialog_content'>
|
||||||
<p>{ _t('Loading device info...') }</p>
|
<p>{ _t('Loading device info...') }</p>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,6 +165,7 @@ export default React.createClass({
|
||||||
<BaseDialog className='mx_KeyShareRequestDialog'
|
<BaseDialog className='mx_KeyShareRequestDialog'
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={_t('Encryption key request')}
|
title={_t('Encryption key request')}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
{ content }
|
{ content }
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
|
@ -60,10 +60,10 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished}
|
||||||
onEnterPressed={this.onOk}
|
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
{ this.props.description }
|
{ this.props.description }
|
||||||
</div>
|
</div>
|
||||||
<DialogButtons primaryButton={this.props.button || _t('OK')}
|
<DialogButtons primaryButton={this.props.button || _t('OK')}
|
||||||
|
|
|
@ -30,6 +30,12 @@ export default React.createClass({
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
if (this.refs.bugreportLink) {
|
||||||
|
this.refs.bugreportLink.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_sendBugReport: function() {
|
_sendBugReport: function() {
|
||||||
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
||||||
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
|
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
|
||||||
|
@ -50,16 +56,20 @@ export default React.createClass({
|
||||||
{ _t(
|
{ _t(
|
||||||
"Otherwise, <a>click here</a> to send a bug report.",
|
"Otherwise, <a>click here</a> to send a bug report.",
|
||||||
{},
|
{},
|
||||||
{ 'a': (sub) => <a onClick={this._sendBugReport} key="bugreport" href='#'>{ sub }</a> },
|
{ 'a': (sub) => <a ref="bugreportLink" onClick={this._sendBugReport}
|
||||||
|
key="bugreport" href='#'>{ sub }</a> },
|
||||||
) }
|
) }
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const shouldFocusContinueButton =!(bugreport==true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
||||||
title={_t('Unable to restore session')}>
|
title={_t('Unable to restore session')}
|
||||||
<div className="mx_Dialog_content">
|
contentId='mx_Dialog_content'
|
||||||
|
>
|
||||||
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
<p>{ _t("We encountered an error trying to restore your previous session. If " +
|
<p>{ _t("We encountered an error trying to restore your previous session. If " +
|
||||||
"you continue, you will need to log in again, and encrypted chat " +
|
"you continue, you will need to log in again, and encrypted chat " +
|
||||||
"history will be unreadable.") }</p>
|
"history will be unreadable.") }</p>
|
||||||
|
@ -71,7 +81,7 @@ export default React.createClass({
|
||||||
{ bugreport }
|
{ bugreport }
|
||||||
</div>
|
</div>
|
||||||
<DialogButtons primaryButton={_t("Continue anyway")}
|
<DialogButtons primaryButton={_t("Continue anyway")}
|
||||||
onPrimaryButtonClick={this._continueClicked}
|
onPrimaryButtonClick={this._continueClicked} focus={shouldFocusContinueButton}
|
||||||
onCancel={this.props.onFinished} />
|
onCancel={this.props.onFinished} />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -41,9 +41,6 @@ export default React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
onEmailAddressChanged: function(value) {
|
onEmailAddressChanged: function(value) {
|
||||||
this.setState({
|
this.setState({
|
||||||
emailAddress: value,
|
emailAddress: value,
|
||||||
|
@ -131,6 +128,7 @@ export default React.createClass({
|
||||||
|
|
||||||
const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText
|
const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText
|
||||||
className="mx_SetEmailDialog_email_input"
|
className="mx_SetEmailDialog_email_input"
|
||||||
|
autoFocus="true"
|
||||||
placeholder={_t("Email address")}
|
placeholder={_t("Email address")}
|
||||||
placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
|
placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
|
||||||
blurToCancel={false}
|
blurToCancel={false}
|
||||||
|
@ -140,9 +138,10 @@ export default React.createClass({
|
||||||
<BaseDialog className="mx_SetEmailDialog"
|
<BaseDialog className="mx_SetEmailDialog"
|
||||||
onFinished={this.onCancelled}
|
onFinished={this.onCancelled}
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<p>
|
<p id='mx_Dialog_content'>
|
||||||
{ _t('This will allow you to reset your password and receive notifications.') }
|
{ _t('This will allow you to reset your password and receive notifications.') }
|
||||||
</p>
|
</p>
|
||||||
{ emailInput }
|
{ emailInput }
|
||||||
|
|
|
@ -235,14 +235,14 @@ export default React.createClass({
|
||||||
"error": Boolean(this.state.usernameError),
|
"error": Boolean(this.state.usernameError),
|
||||||
"success": usernameAvailable,
|
"success": usernameAvailable,
|
||||||
});
|
});
|
||||||
usernameIndicator = <div className={usernameIndicatorClasses}>
|
usernameIndicator = <div className={usernameIndicatorClasses} role="alert">
|
||||||
{ usernameAvailable ? _t('Username available') : this.state.usernameError }
|
{ usernameAvailable ? _t('Username available') : this.state.usernameError }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let authErrorIndicator = null;
|
let authErrorIndicator = null;
|
||||||
if (this.state.authError) {
|
if (this.state.authError) {
|
||||||
authErrorIndicator = <div className="error">
|
authErrorIndicator = <div className="error" role="alert">
|
||||||
{ this.state.authError }
|
{ this.state.authError }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
@ -254,8 +254,9 @@ export default React.createClass({
|
||||||
<BaseDialog className="mx_SetMxIdDialog"
|
<BaseDialog className="mx_SetMxIdDialog"
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={_t('To get started, please pick a username!')}
|
title={_t('To get started, please pick a username!')}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
<div className="mx_SetMxIdDialog_input_group">
|
<div className="mx_SetMxIdDialog_input_group">
|
||||||
<input type="text" ref="input_value" value={this.state.username}
|
<input type="text" ref="input_value" value={this.state.username}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|
|
@ -61,9 +61,9 @@ export default React.createClass({
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_TextInputDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_TextInputDialog" onFinished={this.props.onFinished}
|
||||||
onEnterPressed={this.onOk}
|
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
>
|
>
|
||||||
|
<form onSubmit={this.onOk}>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<div className="mx_TextInputDialog_label">
|
<div className="mx_TextInputDialog_label">
|
||||||
<label htmlFor="textinput"> { this.props.description } </label>
|
<label htmlFor="textinput"> { this.props.description } </label>
|
||||||
|
@ -72,6 +72,7 @@ export default React.createClass({
|
||||||
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" />
|
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
<DialogButtons primaryButton={this.props.button}
|
<DialogButtons primaryButton={this.props.button}
|
||||||
onPrimaryButtonClick={this.onOk}
|
onPrimaryButtonClick={this.onOk}
|
||||||
onCancel={this.onCancel} />
|
onCancel={this.onCancel} />
|
||||||
|
|
|
@ -189,8 +189,9 @@ export default React.createClass({
|
||||||
<BaseDialog className='mx_UnknownDeviceDialog'
|
<BaseDialog className='mx_UnknownDeviceDialog'
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={_t('Room contains unknown devices')}
|
title={_t('Room contains unknown devices')}
|
||||||
|
contentId='mx_Dialog_content'
|
||||||
>
|
>
|
||||||
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
<GeminiScrollbar autoshow={false} className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
<h4>
|
<h4>
|
||||||
{ _t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name}) }
|
{ _t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name}) }
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { KeyCode } from '../../../Keyboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccessibleButton is a generic wrapper for any element that should be treated
|
* AccessibleButton is a generic wrapper for any element that should be treated
|
||||||
* as a button. Identifies the element as a button, setting proper tab
|
* as a button. Identifies the element as a button, setting proper tab
|
||||||
|
@ -28,8 +30,34 @@ import PropTypes from 'prop-types';
|
||||||
export default function AccessibleButton(props) {
|
export default function AccessibleButton(props) {
|
||||||
const {element, onClick, children, ...restProps} = props;
|
const {element, onClick, children, ...restProps} = props;
|
||||||
restProps.onClick = onClick;
|
restProps.onClick = onClick;
|
||||||
|
// We need to consume enter onKeyDown and space onKeyUp
|
||||||
|
// otherwise we are risking also activating other keyboard focusable elements
|
||||||
|
// that might receive focus as a result of the AccessibleButtonClick action
|
||||||
|
// It's because we are using html buttons at a few places e.g. inside dialogs
|
||||||
|
// And divs which we report as role button to assistive technologies.
|
||||||
|
// Browsers handle space and enter keypresses differently and we are only adjusting to the
|
||||||
|
// inconsistencies here
|
||||||
|
restProps.onKeyDown = function(e) {
|
||||||
|
if (e.keyCode === KeyCode.ENTER) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
return onClick(e);
|
||||||
|
}
|
||||||
|
if (e.keyCode === KeyCode.SPACE) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
restProps.onKeyUp = function(e) {
|
restProps.onKeyUp = function(e) {
|
||||||
if (e.keyCode == 13 || e.keyCode == 32) return onClick(e);
|
if (e.keyCode === KeyCode.SPACE) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
return onClick(e);
|
||||||
|
}
|
||||||
|
if (e.keyCode === KeyCode.ENTER) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
restProps.tabIndex = restProps.tabIndex || "0";
|
restProps.tabIndex = restProps.tabIndex || "0";
|
||||||
restProps.role = "button";
|
restProps.role = "button";
|
||||||
|
|
|
@ -128,12 +128,22 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let errorSection;
|
||||||
|
if (this.props.errorText) {
|
||||||
|
errorSection = (
|
||||||
|
<div className="error" role="alert">
|
||||||
|
{ this.props.errorText }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{ _t("To continue, please enter your password.") }</p>
|
<p>{ _t("To continue, please enter your password.") }</p>
|
||||||
<p>{ _t("Password:") }</p>
|
|
||||||
<form onSubmit={this._onSubmit}>
|
<form onSubmit={this._onSubmit}>
|
||||||
|
<label htmlFor="passwordField">{ _t("Password:") }</label>
|
||||||
<input
|
<input
|
||||||
|
name="passwordField"
|
||||||
ref="passwordField"
|
ref="passwordField"
|
||||||
className={passwordBoxClass}
|
className={passwordBoxClass}
|
||||||
onChange={this._onPasswordFieldChange}
|
onChange={this._onPasswordFieldChange}
|
||||||
|
@ -143,9 +153,7 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
{ submitButtonOrSpinner }
|
{ submitButtonOrSpinner }
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div className="error">
|
{ errorSection }
|
||||||
{ this.props.errorText }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -180,14 +188,22 @@ export const RecaptchaAuthEntry = React.createClass({
|
||||||
|
|
||||||
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
|
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
|
||||||
const sitePublicKey = this.props.stageParams.public_key;
|
const sitePublicKey = this.props.stageParams.public_key;
|
||||||
|
|
||||||
|
let errorSection;
|
||||||
|
if (this.props.errorText) {
|
||||||
|
errorSection = (
|
||||||
|
<div className="error" role="alert">
|
||||||
|
{ this.props.errorText }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CaptchaForm sitePublicKey={sitePublicKey}
|
<CaptchaForm sitePublicKey={sitePublicKey}
|
||||||
onCaptchaResponse={this._onCaptchaResponse}
|
onCaptchaResponse={this._onCaptchaResponse}
|
||||||
/>
|
/>
|
||||||
<div className="error">
|
{ errorSection }
|
||||||
{ this.props.errorText }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -372,6 +388,14 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
||||||
mx_UserSettings_button: true, // XXX button classes
|
mx_UserSettings_button: true, // XXX button classes
|
||||||
});
|
});
|
||||||
|
let errorSection;
|
||||||
|
if (this.state.errorText) {
|
||||||
|
errorSection = (
|
||||||
|
<div className="error" role="alert">
|
||||||
|
{ this.state.errorText }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{ _t("A text message has been sent to %(msisdn)s",
|
<p>{ _t("A text message has been sent to %(msisdn)s",
|
||||||
|
@ -385,6 +409,7 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
className="mx_InteractiveAuthEntryComponents_msisdnEntry"
|
className="mx_InteractiveAuthEntryComponents_msisdnEntry"
|
||||||
value={this.state.token}
|
value={this.state.token}
|
||||||
onChange={this._onTokenChange}
|
onChange={this._onTokenChange}
|
||||||
|
aria-label={ _t("Code")}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" value={_t("Submit")}
|
<input type="submit" value={_t("Submit")}
|
||||||
|
@ -392,9 +417,7 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
disabled={!enableSubmit}
|
disabled={!enableSubmit}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<div className="error">
|
{errorSection}
|
||||||
{ this.state.errorText }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -427,6 +450,12 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
focus: function() {
|
||||||
|
if (this.refs.fallbackButton) {
|
||||||
|
this.refs.fallbackButton.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onShowFallbackClick: function() {
|
_onShowFallbackClick: function() {
|
||||||
const url = this.props.matrixClient.getFallbackAuthUrl(
|
const url = this.props.matrixClient.getFallbackAuthUrl(
|
||||||
this.props.loginType,
|
this.props.loginType,
|
||||||
|
@ -445,12 +474,18 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
let errorSection;
|
||||||
<div>
|
if (this.props.errorText) {
|
||||||
<a onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
|
errorSection = (
|
||||||
<div className="error">
|
<div className="error" role="alert">
|
||||||
{ this.props.errorText }
|
{ this.props.errorText }
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<a ref="fallbackButton" onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
|
||||||
|
{errorSection}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -531,6 +531,7 @@
|
||||||
"Token incorrect": "Token incorrect",
|
"Token incorrect": "Token incorrect",
|
||||||
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
||||||
"Please enter the code it contains:": "Please enter the code it contains:",
|
"Please enter the code it contains:": "Please enter the code it contains:",
|
||||||
|
"Code": "Code",
|
||||||
"Start authentication": "Start authentication",
|
"Start authentication": "Start authentication",
|
||||||
"powered by Matrix": "powered by Matrix",
|
"powered by Matrix": "powered by Matrix",
|
||||||
"Username on %(hs)s": "Username on %(hs)s",
|
"Username on %(hs)s": "Username on %(hs)s",
|
||||||
|
|
Loading…
Reference in a new issue