Merge branch 'develop' into travis/granular_bugs

This commit is contained in:
Matthew Hodgson 2017-11-16 13:12:03 +00:00 committed by GitHub
commit f62b04c3be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 392 additions and 232 deletions

View file

@ -18,7 +18,7 @@ import React from 'react';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
export default React.createClass({
@ -45,9 +45,10 @@ export default React.createClass({
if (SdkConfig.get().bug_report_endpoint_url) {
bugreport = (
<p>
{ _tJsx(
{ _t(
"Otherwise, <a>click here</a> to send a bug report.",
/<a>(.*?)<\/a>/, (sub) => <a onClick={this._sendBugReport} key="bugreport" href='#'>{ sub }</a>,
{},
{ 'a': (sub) => <a onClick={this._sendBugReport} key="bugreport" href='#'>{ sub }</a> },
) }
</p>
);

View file

@ -21,7 +21,7 @@ import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import classnames from 'classnames';
import KeyCode from '../../../KeyCode';
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
// The amount of time to wait for further changes to the input username before
// sending a request to the server
@ -267,24 +267,21 @@ export default React.createClass({
</div>
{ usernameIndicator }
<p>
{ _tJsx(
{ _t(
'This will be your account name on the <span></span> ' +
'homeserver, or you can pick a <a>different server</a>.',
[
/<span><\/span>/,
/<a>(.*?)<\/a>/,
],
[
(sub) => <span>{ this.props.homeserverUrl }</span>,
(sub) => <a href="#" onClick={this.props.onDifferentServerClicked}>{ sub }</a>,
],
{},
{
'span': <span>{ this.props.homeserverUrl }</span>,
'a': (sub) => <a href="#" onClick={this.props.onDifferentServerClicked}>{ sub }</a>,
},
) }
</p>
<p>
{ _tJsx(
{ _t(
'If you already have a Matrix account you can <a>log in</a> instead.',
/<a>(.*?)<\/a>/,
[(sub) => <a href="#" onClick={this.props.onLoginClick}>{ sub }</a>],
{},
{ 'a': (sub) => <a href="#" onClick={this.props.onLoginClick}>{ sub }</a> },
) }
</p>
{ auth }

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import ReactDOM from 'react-dom';
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
const DIV_ID = 'mx_recaptcha';
@ -67,10 +67,10 @@ module.exports = React.createClass({
// * jumping straight to a hosted captcha page (but we don't support that yet)
// * embedding the captcha in an iframe (if that works)
// * using a better captcha lib
ReactDOM.render(_tJsx(
ReactDOM.render(_t(
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
/<a>(.*?)<\/a>/,
(sub) => { return <a href='https://riot.im/app'>{ sub }</a>; }), warning);
{},
{ 'a': (sub) => { return <a href='https://riot.im/app'>{ sub }</a>; }}), warning);
this.refs.recaptchaContainer.appendChild(warning);
} else {
const scriptTag = document.createElement('script');

View file

@ -20,7 +20,7 @@ import url from 'url';
import classnames from 'classnames';
import sdk from '../../../index';
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
/* This file contains a collection of components which are used by the
* InteractiveAuth to prompt the user to enter the information needed
@ -256,7 +256,10 @@ export const EmailIdentityAuthEntry = React.createClass({
} else {
return (
<div>
<p>{ _tJsx("An email has been sent to %(emailAddress)s", /%\(emailAddress\)s/, (sub) => <i>{this.props.inputs.emailAddress}</i>) }</p>
<p>{ _t("An email has been sent to %(emailAddress)s",
{ emailAddress: (sub) => <i>{ this.props.inputs.emailAddress }</i> },
) }
</p>
<p>{ _t("Please check your email to continue registration.") }</p>
</div>
);
@ -370,7 +373,10 @@ export const MsisdnAuthEntry = React.createClass({
});
return (
<div>
<p>{ _tJsx("A text message has been sent to %(msisdn)s", /%\(msisdn\)s/, (sub) => <i>{this._msisdn}</i>) }</p>
<p>{ _t("A text message has been sent to %(msisdn)s",
{ msisdn: <i>this._msisdn</i> },
) }
</p>
<p>{ _t("Please enter the code it contains:") }</p>
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
<form onSubmit={this._onFormSubmit}>

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { ContentRepo } from 'matrix-js-sdk';
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import Modal from '../../../Modal';
import AccessibleButton from '../elements/AccessibleButton';
@ -67,24 +67,17 @@ module.exports = React.createClass({
'crop',
);
// it sucks that _tJsx doesn't support normal _t substitutions :((
return (
<div className="mx_RoomAvatarEvent">
{ _tJsx('%(senderDisplayName)s changed the room avatar to <img/>',
[
/%\(senderDisplayName\)s/,
/<img\/>/,
],
[
(sub) => senderDisplayName,
(sub) =>
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
onClick={this.onAvatarClick.bind(this, name)}>
<BaseAvatar width={14} height={14} url={url}
name={name} />
</AccessibleButton>,
],
)
{ _t('%(senderDisplayName)s changed the room avatar to <img/>',
{ senderDisplayName: senderDisplayName },
{
'img': () =>
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
onClick={this.onAvatarClick.bind(this, name)}>
<BaseAvatar width={14} height={14} url={url} name={name} />
</AccessibleButton>,
})
}
</div>
);

View file

@ -19,7 +19,7 @@
import React from 'react';
import sdk from '../../../index';
import Flair from '../elements/Flair.js';
import { _tJsx } from '../../../languageHandler';
import { _t, substitute } from '../../../languageHandler';
export default function SenderProfile(props) {
const EmojiText = sdk.getComponent('elements.EmojiText');
@ -42,22 +42,28 @@ export default function SenderProfile(props) {
: null,
];
let content = '';
let content;
if(props.text) {
// Replace senderName, and wrap surrounding text in spans with the right class
content = _tJsx(props.text, /^(.*)\%\(senderName\)s(.*)$/m, (p1, p2) => [
p1 ? <span className='mx_SenderProfile_aux'>{ p1 }</span> : null,
nameElem,
p2 ? <span className='mx_SenderProfile_aux'>{ p2 }</span> : null,
]);
content = _t(props.text, { senderName: () => nameElem });
} else {
content = nameElem;
// There is nothing to translate here, so call substitute() instead
content = substitute('%(senderName)s', { senderName: () => nameElem });
}
// The text surrounding the user name must be wrapped in order for it to have the correct opacity.
// It is not possible to wrap the whole thing, because the user name might contain flair which should
// be shown at full opacity. Sadly CSS does not make it possible to "reset" opacity so we have to do it
// in parts like this. Sometimes CSS makes me a sad panda :-(
// XXX: This could be avoided if the actual colour is set, rather than faking it with opacity
return (
<div className="mx_SenderProfile" dir="auto" onClick={props.onClick}>
{ content }
{ content.props.children[0] ?
<span className='mx_SenderProfile_aux'>{ content.props.children[0] }</span> : ''
}
{ content.props.children[1] }
{ content.props.children[2] ?
<span className='mx_SenderProfile_aux'>{ content.props.children[2] }</span> : ''
}
</div>
);
}

View file

@ -17,7 +17,7 @@ limitations under the License.
const React = require('react');
const sdk = require("../../../index");
import {_t, _td, _tJsx} from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
@ -42,11 +42,11 @@ module.exports = React.createClass({
let previewsForAccount = null;
if (SettingsStore.getValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled")) {
previewsForAccount = (
_tJsx("You have <a>enabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
_t("You have <a>enabled</a> URL previews by default.", {}, { 'a': (sub)=><a href="#/settings">{ sub }</a> })
);
} else {
previewsForAccount = (
_tJsx("You have <a>disabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
_t("You have <a>disabled</a> URL previews by default.", {}, { 'a': (sub)=><a href="#/settings">{ sub }</a> })
);
}
@ -70,7 +70,7 @@ module.exports = React.createClass({
previewsForRoom = (<label>{ _t(str) }</label>);
}
let previewsForRoomAccount = (
const previewsForRoomAccount = (
<SettingsFlag name="urlPreviewsEnabled"
level={SettingLevel.ROOM_ACCOUNT}
roomId={this.props.room.roomId}

View file

@ -21,7 +21,7 @@ import sdk from '../../../index';
import dis from "../../../dispatcher";
import ObjectUtils from '../../../ObjectUtils';
import AppsDrawer from './AppsDrawer';
import { _t, _tJsx} from '../../../languageHandler';
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -99,13 +99,13 @@ module.exports = React.createClass({
supportedText = _t(" (unsupported)");
} else {
joinNode = (<span>
{ _tJsx(
{ _t(
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
[/<voiceText>(.*?)<\/voiceText>/, /<videoText>(.*?)<\/videoText>/],
[
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{ sub }</a>,
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{ sub }</a>,
],
{},
{
'voiceText': (sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{ sub }</a>,
'videoText': (sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{ sub }</a>,
},
) }
</span>);
}

View file

@ -33,22 +33,30 @@ const ObjectUtils = require('../../../ObjectUtils');
const eventTileTypes = {
'm.room.message': 'messages.MessageEvent',
'm.room.member': 'messages.TextualEvent',
'm.call.invite': 'messages.TextualEvent',
'm.call.answer': 'messages.TextualEvent',
'm.call.hangup': 'messages.TextualEvent',
};
const stateEventTileTypes = {
'm.room.member': 'messages.TextualEvent',
'm.room.name': 'messages.TextualEvent',
'm.room.avatar': 'messages.RoomAvatarEvent',
'm.room.topic': 'messages.TextualEvent',
'm.room.third_party_invite': 'messages.TextualEvent',
'm.room.history_visibility': 'messages.TextualEvent',
'm.room.encryption': 'messages.TextualEvent',
'm.room.topic': 'messages.TextualEvent',
'm.room.power_levels': 'messages.TextualEvent',
'm.room.pinned_events' : 'messages.TextualEvent',
'm.room.pinned_events': 'messages.TextualEvent',
'im.vector.modular.widgets': 'messages.TextualEvent',
};
function getHandlerTile(ev) {
const type = ev.getType();
return ev.isState() ? stateEventTileTypes[type] : eventTileTypes[type];
}
const MAX_READ_AVATARS = 5;
// Our component structure for EventTiles on the timeline is:
@ -433,7 +441,7 @@ module.exports = withMatrixClient(React.createClass({
// Info messages are basically information about commands processed on a room
const isInfoMessage = (eventType !== 'm.room.message');
const EventTileType = sdk.getComponent(eventTileTypes[eventType]);
const EventTileType = sdk.getComponent(getHandlerTile(this.props.mxEvent));
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if (!EventTileType) {
@ -600,8 +608,10 @@ module.exports = withMatrixClient(React.createClass({
module.exports.haveTileForEvent = function(e) {
// Only messages have a tile (black-rectangle) if redacted
if (e.isRedacted() && e.getType() !== 'm.room.message') return false;
if (eventTileTypes[e.getType()] == undefined) return false;
if (eventTileTypes[e.getType()] == 'messages.TextualEvent') {
const handler = getHandlerTile(e);
if (handler === undefined) return false;
if (handler === 'messages.TextualEvent') {
return TextForEvent.textForEvent(e) !== '';
} else {
return true;

View file

@ -18,12 +18,10 @@ limitations under the License.
'use strict';
const React = require("react");
const ReactDOM = require("react-dom");
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
const GeminiScrollbar = require('react-gemini-scrollbar');
const MatrixClientPeg = require("../../../MatrixClientPeg");
const CallHandler = require('../../../CallHandler');
const RoomListSorter = require("../../../RoomListSorter");
const Unread = require('../../../Unread');
const dis = require("../../../dispatcher");
const sdk = require('../../../index');
const rate_limited_func = require('../../../ratelimitedfunc');
@ -486,28 +484,25 @@ module.exports = React.createClass({
const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg');
switch (section) {
case 'im.vector.fake.direct':
return <div className="mx_RoomList_emptySubListTip">
{ _tJsx(
{ _t(
"Press <StartChatButton> to start a chat with someone",
[/<StartChatButton>/],
[
(sub) => <StartChatButton size="16" callout={true} />,
],
{},
{ 'StartChatButton': <StartChatButton size="16" callout={true} /> },
) }
</div>;
case 'im.vector.fake.recent':
return <div className="mx_RoomList_emptySubListTip">
{ _tJsx(
{ _t(
"You're not in any rooms yet! Press <CreateRoomButton> to make a room or"+
" <RoomDirectoryButton> to browse the directory",
[/<CreateRoomButton>/, /<RoomDirectoryButton>/],
[
(sub) => <CreateRoomButton size="16" callout={true} />,
(sub) => <RoomDirectoryButton size="16" callout={true} />,
],
{},
{
'CreateRoomButton': <CreateRoomButton size="16" callout={true} />,
'RoomDirectoryButton': <RoomDirectoryButton size="16" callout={true} />,
},
) }
</div>;
}

View file

@ -21,7 +21,7 @@ const React = require('react');
const sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
import { _t, _tJsx } from '../../../languageHandler';
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'RoomPreviewBar',
@ -135,13 +135,13 @@ module.exports = React.createClass({
{ _t('You have been invited to join this room by %(inviterName)s', {inviterName: this.props.inviterName}) }
</div>
<div className="mx_RoomPreviewBar_join_text">
{ _tJsx(
{ _t(
'Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?',
[/<acceptText>(.*?)<\/acceptText>/, /<declineText>(.*?)<\/declineText>/],
[
(sub) => <a onClick={this.props.onJoinClick}>{ sub }</a>,
(sub) => <a onClick={this.props.onRejectClick}>{ sub }</a>,
],
{},
{
'acceptText': (sub) => <a onClick={this.props.onJoinClick}>{ sub }</a>,
'declineText': (sub) => <a onClick={this.props.onRejectClick}>{ sub }</a>,
},
) }
</div>
{ emailMatchBlock }
@ -211,9 +211,9 @@ module.exports = React.createClass({
<div className="mx_RoomPreviewBar_join_text">
{ name ? _t('You are trying to access %(roomName)s.', {roomName: name}) : _t('You are trying to access a room.') }
<br />
{ _tJsx("<a>Click here</a> to join the discussion!",
/<a>(.*?)<\/a>/,
(sub) => <a onClick={this.props.onJoinClick}><b>{ sub }</b></a>,
{ _t("<a>Click here</a> to join the discussion!",
{},
{ 'a': (sub) => <a onClick={this.props.onJoinClick}><b>{ sub }</b></a> },
) }
</div>
</div>

View file

@ -17,7 +17,7 @@ limitations under the License.
import Promise from 'bluebird';
import React from 'react';
import { _t, _tJsx, _td } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
import Modal from '../../../Modal';
@ -628,9 +628,7 @@ module.exports = React.createClass({
const ColorSettings = sdk.getComponent("room_settings.ColorSettings");
const UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings");
const RelatedGroupSettings = sdk.getComponent("room_settings.RelatedGroupSettings");
const EditableText = sdk.getComponent('elements.EditableText');
const PowerSelector = sdk.getComponent('elements.PowerSelector');
const Loader = sdk.getComponent("elements.Spinner");
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
@ -751,7 +749,7 @@ module.exports = React.createClass({
var tagsSection = null;
if (canSetTag || self.state.tags) {
var tagsSection =
tagsSection =
<div className="mx_RoomSettings_tags">
{ _t("Tagged as: ") }{ canSetTag ?
(tags.map(function(tag, i) {
@ -781,10 +779,10 @@ module.exports = React.createClass({
if (this.state.join_rule === "public" && aliasCount == 0) {
addressWarning =
<div className="mx_RoomSettings_warning">
{ _tJsx(
{ _t(
'To link to a room it must have <a>an address</a>.',
/<a>(.*?)<\/a>/,
(sub) => <a href="#addresses">{ sub }</a>,
{},
{ 'a': (sub) => <a href="#addresses">{ sub }</a> },
) }
</div>;
}
@ -931,7 +929,7 @@ module.exports = React.createClass({
{ Object.keys(events_levels).map(function(event_type, i) {
let label = plEventsToLabels[event_type];
if (label) label = _t(label);
else label = _tJsx("To send events of type <eventType/>, you must be a", /<eventType\/>/, () => <code>{ event_type }</code>);
else label = _t("To send events of type <eventType/>, you must be a", {}, { 'eventType': <code>{ event_type }</code> });
return (
<div className="mx_RoomSettings_powerLevel" key={event_type}>
<span className="mx_RoomSettings_powerLevelKey">{ label } </span>