Merge branch 'develop' into travis/pinned_messages

This commit is contained in:
Travis Ralston 2017-10-14 16:10:32 -06:00
commit c34b55c6c7
185 changed files with 4991 additions and 3543 deletions

View file

@ -1,10 +1,7 @@
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
src/async-components/views/dialogs/EncryptedEventDialog.js
src/autocomplete/AutocompleteProvider.js
src/autocomplete/Autocompleter.js
src/autocomplete/Components.js
src/autocomplete/DuckDuckGoProvider.js
src/autocomplete/EmojiProvider.js
src/autocomplete/UserProvider.js
src/CallHandler.js
@ -12,11 +9,9 @@ src/component-index.js
src/components/structures/ContextualMenu.js
src/components/structures/CreateRoom.js
src/components/structures/FilePanel.js
src/components/structures/InteractiveAuth.js
src/components/structures/LoggedInView.js
src/components/structures/login/ForgotPassword.js
src/components/structures/login/Login.js
src/components/structures/login/PostRegistration.js
src/components/structures/login/Registration.js
src/components/structures/MessagePanel.js
src/components/structures/NotificationPanel.js
@ -27,51 +22,32 @@ src/components/structures/TimelinePanel.js
src/components/structures/UploadBar.js
src/components/views/avatars/BaseAvatar.js
src/components/views/avatars/MemberAvatar.js
src/components/views/avatars/RoomAvatar.js
src/components/views/create_room/CreateRoomButton.js
src/components/views/create_room/Presets.js
src/components/views/create_room/RoomAlias.js
src/components/views/dialogs/ChatCreateOrReuseDialog.js
src/components/views/dialogs/DeactivateAccountDialog.js
src/components/views/dialogs/InteractiveAuthDialog.js
src/components/views/dialogs/UnknownDeviceDialog.js
src/components/views/elements/AccessibleButton.js
src/components/views/elements/ActionButton.js
src/components/views/elements/AddressSelector.js
src/components/views/elements/AddressTile.js
src/components/views/elements/CreateRoomButton.js
src/components/views/elements/DeviceVerifyButtons.js
src/components/views/elements/DirectorySearchBox.js
src/components/views/elements/Dropdown.js
src/components/views/elements/EditableText.js
src/components/views/elements/EditableTextContainer.js
src/components/views/elements/HomeButton.js
src/components/views/elements/LanguageDropdown.js
src/components/views/elements/MemberEventListSummary.js
src/components/views/elements/PowerSelector.js
src/components/views/elements/ProgressBar.js
src/components/views/elements/RoomDirectoryButton.js
src/components/views/elements/SettingsButton.js
src/components/views/elements/StartChatButton.js
src/components/views/elements/TintableSvg.js
src/components/views/elements/UserSelector.js
src/components/views/login/CaptchaForm.js
src/components/views/login/CasLogin.js
src/components/views/login/CountryDropdown.js
src/components/views/login/CustomServerDialog.js
src/components/views/login/InteractiveAuthEntryComponents.js
src/components/views/login/LoginHeader.js
src/components/views/login/PasswordLogin.js
src/components/views/login/RegistrationForm.js
src/components/views/login/ServerConfig.js
src/components/views/messages/MAudioBody.js
src/components/views/messages/MessageEvent.js
src/components/views/messages/MFileBody.js
src/components/views/messages/MImageBody.js
src/components/views/messages/MVideoBody.js
src/components/views/messages/RoomAvatarEvent.js
src/components/views/messages/TextualBody.js
src/components/views/messages/TextualEvent.js
src/components/views/room_settings/AliasSettings.js
src/components/views/room_settings/ColorSettings.js
src/components/views/room_settings/UrlPreviewSettings.js
@ -86,14 +62,11 @@ src/components/views/rooms/MemberList.js
src/components/views/rooms/MemberTile.js
src/components/views/rooms/MessageComposer.js
src/components/views/rooms/MessageComposerInput.js
src/components/views/rooms/PresenceLabel.js
src/components/views/rooms/ReadReceiptMarker.js
src/components/views/rooms/RoomList.js
src/components/views/rooms/RoomNameEditor.js
src/components/views/rooms/RoomPreviewBar.js
src/components/views/rooms/RoomSettings.js
src/components/views/rooms/RoomTile.js
src/components/views/rooms/RoomTopicEditor.js
src/components/views/rooms/SearchableEntityList.js
src/components/views/rooms/SearchResultTile.js
src/components/views/rooms/TopUnreadMessagesBar.js
@ -103,8 +76,6 @@ src/components/views/settings/ChangeAvatar.js
src/components/views/settings/ChangeDisplayName.js
src/components/views/settings/ChangePassword.js
src/components/views/settings/DevicesPanel.js
src/components/views/settings/DevicesPanelEntry.js
src/components/views/settings/EnableNotificationsButton.js
src/ContentMessages.js
src/HtmlUtils.js
src/ImageUtils.js
@ -122,7 +93,6 @@ src/RichText.js
src/Roles.js
src/Rooms.js
src/ScalarAuthClient.js
src/ScalarMessaging.js
src/Tinter.js
src/UiEffects.js
src/Unread.js
@ -135,17 +105,13 @@ src/Velociraptor.js
src/VelocityBounce.js
src/WhoIsTyping.js
src/wrappers/withMatrixClient.js
test/all-tests.js
test/components/structures/login/Registration-test.js
test/components/structures/MessagePanel-test.js
test/components/structures/ScrollPanel-test.js
test/components/structures/TimelinePanel-test.js
test/components/stub-component.js
test/components/views/dialogs/InteractiveAuthDialog-test.js
test/components/views/elements/MemberEventListSummary-test.js
test/components/views/login/RegistrationForm-test.js
test/components/views/rooms/MessageComposerInput-test.js
test/mock-clock.js
test/skinned-sdk.js
test/stores/RoomViewStore-test.js
test/test-utils.js

View file

@ -63,23 +63,22 @@ import dis from './dispatcher';
global.mxCalls = {
//room_id: MatrixCall
};
var calls = global.mxCalls;
var ConferenceHandler = null;
const calls = global.mxCalls;
let ConferenceHandler = null;
var audioPromises = {};
const audioPromises = {};
function play(audioId) {
// TODO: Attach an invisible element for this instead
// which listens?
var audio = document.getElementById(audioId);
const audio = document.getElementById(audioId);
if (audio) {
if (audioPromises[audioId]) {
audioPromises[audioId] = audioPromises[audioId].then(()=>{
audio.load();
return audio.play();
});
}
else {
} else {
audioPromises[audioId] = audio.play();
}
}
@ -88,12 +87,11 @@ function play(audioId) {
function pause(audioId) {
// TODO: Attach an invisible element for this instead
// which listens?
var audio = document.getElementById(audioId);
const audio = document.getElementById(audioId);
if (audio) {
if (audioPromises[audioId]) {
audioPromises[audioId] = audioPromises[audioId].then(()=>audio.pause());
}
else {
} else {
// pause doesn't actually return a promise, but might as well do this for symmetry with play();
audioPromises[audioId] = audio.pause();
}
@ -125,38 +123,32 @@ function _setCallListeners(call) {
if (newState === "ringing") {
_setCallState(call, call.roomId, "ringing");
pause("ringbackAudio");
}
else if (newState === "invite_sent") {
} else if (newState === "invite_sent") {
_setCallState(call, call.roomId, "ringback");
play("ringbackAudio");
}
else if (newState === "ended" && oldState === "connected") {
} else if (newState === "ended" && oldState === "connected") {
_setCallState(undefined, call.roomId, "ended");
pause("ringbackAudio");
play("callendAudio");
}
else if (newState === "ended" && oldState === "invite_sent" &&
} else if (newState === "ended" && oldState === "invite_sent" &&
(call.hangupParty === "remote" ||
(call.hangupParty === "local" && call.hangupReason === "invite_timeout")
)) {
_setCallState(call, call.roomId, "busy");
pause("ringbackAudio");
play("busyAudio");
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Call Handler', 'Call Timeout', ErrorDialog, {
title: _t('Call Timeout'),
description: _t('The remote side failed to pick up') + '.',
});
}
else if (oldState === "invite_sent") {
} else if (oldState === "invite_sent") {
_setCallState(call, call.roomId, "stop_ringback");
pause("ringbackAudio");
}
else if (oldState === "ringing") {
} else if (oldState === "ringing") {
_setCallState(call, call.roomId, "stop_ringing");
pause("ringbackAudio");
}
else if (newState === "connected") {
} else if (newState === "connected") {
_setCallState(call, call.roomId, "connected");
pause("ringbackAudio");
}
@ -165,14 +157,13 @@ function _setCallListeners(call) {
function _setCallState(call, roomId, status) {
console.log(
"Call state in %s changed to %s (%s)", roomId, status, (call ? call.call_state : "-")
"Call state in %s changed to %s (%s)", roomId, status, (call ? call.call_state : "-"),
);
calls[roomId] = call;
if (status === "ringing") {
play("ringAudio");
}
else if (call && call.call_state === "ringing") {
} else if (call && call.call_state === "ringing") {
pause("ringAudio");
}
@ -192,14 +183,12 @@ function _onAction(payload) {
_setCallState(newCall, newCall.roomId, "ringback");
if (payload.type === 'voice') {
newCall.placeVoiceCall();
}
else if (payload.type === 'video') {
} else if (payload.type === 'video') {
newCall.placeVideoCall(
payload.remote_element,
payload.local_element
payload.local_element,
);
}
else if (payload.type === 'screensharing') {
} else if (payload.type === 'screensharing') {
const screenCapErrorString = PlatformPeg.get().screenCaptureErrorString();
if (screenCapErrorString) {
_setCallState(undefined, newCall.roomId, "ended");
@ -213,10 +202,9 @@ function _onAction(payload) {
}
newCall.placeScreenSharingCall(
payload.remote_element,
payload.local_element
payload.local_element,
);
}
else {
} else {
console.error("Unknown conf call type: %s", payload.type);
}
}
@ -255,21 +243,19 @@ function _onAction(payload) {
description: _t('You cannot place a call with yourself.'),
});
return;
}
else if (members.length === 2) {
} else if (members.length === 2) {
console.log("Place %s call in %s", payload.type, payload.room_id);
const call = Matrix.createNewMatrixCall(MatrixClientPeg.get(), payload.room_id, {
forceTURN: UserSettingsStore.getLocalSetting('webRtcForceTURN', false),
});
placeCall(call);
}
else { // > 2
} else { // > 2
dis.dispatch({
action: "place_conference_call",
room_id: payload.room_id,
type: payload.type,
remote_element: payload.remote_element,
local_element: payload.local_element
local_element: payload.local_element,
});
}
break;
@ -280,15 +266,13 @@ function _onAction(payload) {
Modal.createTrackedDialog('Call Handler', 'Conference call unsupported client', ErrorDialog, {
description: _t('Conference calls are not supported in this client'),
});
}
else if (!MatrixClientPeg.get().supportsVoip()) {
} else if (!MatrixClientPeg.get().supportsVoip()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, {
title: _t('VoIP is unsupported'),
description: _t('You cannot place VoIP calls in this browser.'),
});
}
else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
} else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
// Conference calls are implemented by sending the media to central
// server which combines the audio from all the participants together
// into a single stream. This is incompatible with end-to-end encryption
@ -299,16 +283,15 @@ function _onAction(payload) {
Modal.createTrackedDialog('Call Handler', 'Conference calls unsupported e2e', ErrorDialog, {
description: _t('Conference calls are not supported in encrypted rooms'),
});
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Call Handler', 'Conference calling in development', QuestionDialog, {
title: _t('Warning!'),
description: _t('Conference calling is in development and may not be reliable.'),
onFinished: confirm=>{
onFinished: (confirm)=>{
if (confirm) {
ConferenceHandler.createNewMatrixCall(
MatrixClientPeg.get(), payload.room_id
MatrixClientPeg.get(), payload.room_id,
).done(function(call) {
placeCall(call);
}, function(err) {
@ -357,7 +340,7 @@ function _onAction(payload) {
_setCallState(calls[payload.room_id], payload.room_id, "connected");
dis.dispatch({
action: "view_room",
room_id: payload.room_id
room_id: payload.room_id,
});
break;
}
@ -368,9 +351,9 @@ if (!global.mxCallHandler) {
dis.register(_onAction);
}
var callHandler = {
const callHandler = {
getCallForRoom: function(roomId) {
var call = module.exports.getCall(roomId);
let call = module.exports.getCall(roomId);
if (call) return call;
if (ConferenceHandler) {
@ -386,8 +369,8 @@ var callHandler = {
},
getAnyActiveCall: function() {
var roomsWithCalls = Object.keys(calls);
for (var i = 0; i < roomsWithCalls.length; i++) {
const roomsWithCalls = Object.keys(calls);
for (let i = 0; i < roomsWithCalls.length; i++) {
if (calls[roomsWithCalls[i]] &&
calls[roomsWithCalls[i]].call_state !== "ended") {
return calls[roomsWithCalls[i]];
@ -402,7 +385,7 @@ var callHandler = {
getConferenceHandler: function() {
return ConferenceHandler;
}
},
};
// Only things in here which actually need to be global are the
// calls list (done separately) and making sure we only register

View file

@ -17,14 +17,14 @@ limitations under the License.
'use strict';
import Promise from 'bluebird';
var extend = require('./extend');
var dis = require('./dispatcher');
var MatrixClientPeg = require('./MatrixClientPeg');
var sdk = require('./index');
const extend = require('./extend');
const dis = require('./dispatcher');
const MatrixClientPeg = require('./MatrixClientPeg');
const sdk = require('./index');
import { _t } from './languageHandler';
var Modal = require('./Modal');
const Modal = require('./Modal');
var encrypt = require("browser-encrypt-attachment");
const encrypt = require("browser-encrypt-attachment");
// Polyfill for Canvas.toBlob API using Canvas.toDataURL
require("blueimp-canvas-to-blob");
@ -54,8 +54,8 @@ const MAX_HEIGHT = 600;
function createThumbnail(element, inputWidth, inputHeight, mimeType) {
const deferred = Promise.defer();
var targetWidth = inputWidth;
var targetHeight = inputHeight;
let targetWidth = inputWidth;
let targetHeight = inputHeight;
if (targetHeight > MAX_HEIGHT) {
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
targetHeight = MAX_HEIGHT;
@ -81,7 +81,7 @@ function createThumbnail(element, inputWidth, inputHeight, mimeType) {
w: inputWidth,
h: inputHeight,
},
thumbnail: thumbnail
thumbnail: thumbnail,
});
}, mimeType);
@ -129,12 +129,12 @@ function loadImageElement(imageFile) {
* @return {Promise} A promise that resolves with the attachment info.
*/
function infoForImageFile(matrixClient, roomId, imageFile) {
var thumbnailType = "image/png";
let thumbnailType = "image/png";
if (imageFile.type == "image/jpeg") {
thumbnailType = "image/jpeg";
}
var imageInfo;
let imageInfo;
return loadImageElement(imageFile).then(function(img) {
return createThumbnail(img, img.width, img.height, thumbnailType);
}).then(function(result) {
@ -191,7 +191,7 @@ function loadVideoElement(videoFile) {
function infoForVideoFile(matrixClient, roomId, videoFile) {
const thumbnailType = "image/jpeg";
var videoInfo;
let videoInfo;
return loadVideoElement(videoFile).then(function(video) {
return createThumbnail(video, video.videoWidth, video.videoHeight, thumbnailType);
}).then(function(result) {
@ -286,7 +286,7 @@ class ContentMessages {
body: file.name || 'Attachment',
info: {
size: file.size,
}
},
};
// if we have a mime type for the file, add it to the message metadata
@ -297,10 +297,10 @@ class ContentMessages {
const def = Promise.defer();
if (file.type.indexOf('image/') == 0) {
content.msgtype = 'm.image';
infoForImageFile(matrixClient, roomId, file).then(imageInfo=>{
infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{
extend(content.info, imageInfo);
def.resolve();
}, error=>{
}, (error)=>{
console.error(error);
content.msgtype = 'm.file';
def.resolve();
@ -310,10 +310,10 @@ class ContentMessages {
def.resolve();
} else if (file.type.indexOf('video/') == 0) {
content.msgtype = 'm.video';
infoForVideoFile(matrixClient, roomId, file).then(videoInfo=>{
infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{
extend(content.info, videoInfo);
def.resolve();
}, error=>{
}, (error)=>{
content.msgtype = 'm.file';
def.resolve();
});
@ -331,7 +331,7 @@ class ContentMessages {
this.inprogress.push(upload);
dis.dispatch({action: 'upload_started'});
var error;
let error;
function onProgress(ev) {
upload.total = ev.total;
@ -355,11 +355,11 @@ class ContentMessages {
}, function(err) {
error = err;
if (!upload.canceled) {
var desc = _t('The file \'%(fileName)s\' failed to upload', {fileName: upload.fileName}) + '.';
let desc = _t('The file \'%(fileName)s\' failed to upload', {fileName: upload.fileName}) + '.';
if (err.http_status == 413) {
desc = _t('The file \'%(fileName)s\' exceeds this home server\'s size limit for uploads', {fileName: upload.fileName});
}
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Upload failed', '', ErrorDialog, {
title: _t('Upload Failed'),
description: desc,
@ -367,8 +367,8 @@ class ContentMessages {
}
}).finally(() => {
const inprogressKeys = Object.keys(this.inprogress);
for (var i = 0; i < this.inprogress.length; ++i) {
var k = inprogressKeys[i];
for (let i = 0; i < this.inprogress.length; ++i) {
const k = inprogressKeys[i];
if (this.inprogress[k].promise === upload.promise) {
this.inprogress.splice(k, 1);
break;
@ -376,8 +376,7 @@ class ContentMessages {
}
if (error) {
dis.dispatch({action: 'upload_failed', upload: upload});
}
else {
} else {
dis.dispatch({action: 'upload_finished', upload: upload});
}
});
@ -389,9 +388,9 @@ class ContentMessages {
cancelUpload(promise) {
const inprogressKeys = Object.keys(this.inprogress);
var upload;
for (var i = 0; i < this.inprogress.length; ++i) {
var k = inprogressKeys[i];
let upload;
for (let i = 0; i < this.inprogress.length; ++i) {
const k = inprogressKeys[i];
if (this.inprogress[k].promise === promise) {
upload = this.inprogress[k];
break;

View file

@ -19,6 +19,7 @@ import sdk from './';
import MultiInviter from './utils/MultiInviter';
import { _t } from './languageHandler';
import MatrixClientPeg from './MatrixClientPeg';
import GroupStoreCache from './stores/GroupStoreCache';
export function showGroupInviteDialog(groupId) {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
@ -75,6 +76,13 @@ function _onGroupInviteFinished(groupId, addrs) {
title: _t("Failed to invite the following users to %(groupId)s:", {groupId: groupId}),
description: errorList.join(", "),
});
} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Group invitations sent', '', QuestionDialog, {
title: _t("Invites sent"),
description: _t("Your group invitations have been sent."),
hasCancelButton: false,
});
}
}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -86,10 +94,11 @@ function _onGroupInviteFinished(groupId, addrs) {
}
function _onGroupAddRoomFinished(groupId, addrs) {
const groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
const errorList = [];
return Promise.all(addrs.map((addr) => {
return MatrixClientPeg.get()
.addRoomToGroup(groupId, addr.address)
return groupStore
.addRoomToGroup(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
})).then(() => {

View file

@ -17,10 +17,10 @@ limitations under the License.
'use strict';
var React = require('react');
var sanitizeHtml = require('sanitize-html');
var highlight = require('highlight.js');
var linkifyMatrix = require('./linkify-matrix');
const React = require('react');
const sanitizeHtml = require('sanitize-html');
const highlight = require('highlight.js');
const linkifyMatrix = require('./linkify-matrix');
import escape from 'lodash/escape';
import emojione from 'emojione';
import classNames from 'classnames';
@ -66,8 +66,7 @@ function unicodeToImage(str) {
if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) {
// if the unicodeChar doesnt exist just return the entire match
return unicodeChar;
}
else {
} else {
// get the unicode codepoint from the actual char
unicode = emojione.jsEscapeMap[unicodeChar];
@ -183,21 +182,19 @@ const sanitizeHtmlParams = {
if (attribs.href) {
attribs.target = '_blank'; // by default
var m;
let m;
// FIXME: horrible duplication with linkify-matrix
m = attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN);
if (m) {
attribs.href = m[1];
delete attribs.target;
}
else {
} else {
m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
if (m) {
var entity = m[1];
const entity = m[1];
if (entity[0] === '@') {
attribs.href = '#/user/' + entity;
}
else if (entity[0] === '#' || entity[0] === '!') {
} else if (entity[0] === '#' || entity[0] === '!') {
attribs.href = '#/room/' + entity;
}
delete attribs.target;
@ -205,7 +202,7 @@ const sanitizeHtmlParams = {
}
}
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
return { tagName: tagName, attribs : attribs };
return { tagName: tagName, attribs: attribs };
},
'img': function(tagName, attribs) {
// Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag
@ -224,7 +221,7 @@ const sanitizeHtmlParams = {
'code': function(tagName, attribs) {
if (typeof attribs.class !== 'undefined') {
// Filter out all classes other than ones starting with language- for syntax highlighting.
let classes = attribs.class.split(/\s+/).filter(function(cl) {
const classes = attribs.class.split(/\s+/).filter(function(cl) {
return cl.startsWith('language-');
});
attribs.class = classes.join(' ');
@ -287,11 +284,11 @@ class BaseHighlighter {
* TextHighlighter).
*/
applyHighlights(safeSnippet, safeHighlights) {
var lastOffset = 0;
var offset;
var nodes = [];
let lastOffset = 0;
let offset;
let nodes = [];
var safeHighlight = safeHighlights[0];
const safeHighlight = safeHighlights[0];
while ((offset = safeSnippet.toLowerCase().indexOf(safeHighlight.toLowerCase(), lastOffset)) >= 0) {
// handle preamble
if (offset > lastOffset) {
@ -301,7 +298,7 @@ class BaseHighlighter {
// do highlight. use the original string rather than safeHighlight
// to preserve the original casing.
var endOffset = offset + safeHighlight.length;
const endOffset = offset + safeHighlight.length;
nodes.push(this._processSnippet(safeSnippet.substring(offset, endOffset), true));
lastOffset = endOffset;
@ -319,8 +316,7 @@ class BaseHighlighter {
if (safeHighlights[1]) {
// recurse into this range to check for the next set of highlight matches
return this.applyHighlights(safeSnippet, safeHighlights.slice(1));
}
else {
} else {
// no more highlights to be found, just return the unhighlighted string
return [this._processSnippet(safeSnippet, false)];
}
@ -341,7 +337,7 @@ class HtmlHighlighter extends BaseHighlighter {
return snippet;
}
var span = "<span class=\""+this.highlightClass+"\">"
let span = "<span class=\""+this.highlightClass+"\">"
+ snippet + "</span>";
if (this.highlightLink) {
@ -366,15 +362,15 @@ class TextHighlighter extends BaseHighlighter {
* returns a React node
*/
_processSnippet(snippet, highlight) {
var key = this._key++;
const key = this._key++;
var node =
<span key={key} className={highlight ? this.highlightClass : null }>
let node =
<span key={key} className={highlight ? this.highlightClass : null}>
{ snippet }
</span>;
if (highlight && this.highlightLink) {
node = <a key={key} href={this.highlightLink}>{node}</a>;
node = <a key={key} href={this.highlightLink}>{ node }</a>;
}
return node;
@ -389,24 +385,23 @@ class TextHighlighter extends BaseHighlighter {
* highlights: optional list of words to highlight, ordered by longest word first
*
* opts.highlightLink: optional href to add to highlighted words
* opts.disableBigEmoji: optional argument to disable the big emoji class.
*/
export function bodyToHtml(content, highlights, opts) {
opts = opts || {};
var isHtml = (content.format === "org.matrix.custom.html");
let body = isHtml ? content.formatted_body : escape(content.body);
export function bodyToHtml(content, highlights, opts={}) {
const isHtml = (content.format === "org.matrix.custom.html");
const body = isHtml ? content.formatted_body : escape(content.body);
let bodyHasEmoji = false;
var safeBody;
let safeBody;
// XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying
// to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which
// are interrupted by HTML tags (not that we did before) - e.g. foo<span/>bar won't get highlighted
// by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either
try {
if (highlights && highlights.length > 0) {
var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
var safeHighlights = highlights.map(function(highlight) {
const highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
const safeHighlights = highlights.map(function(highlight) {
return sanitizeHtml(highlight, sanitizeHtmlParams);
});
// XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure.
@ -417,16 +412,15 @@ export function bodyToHtml(content, highlights, opts) {
safeBody = sanitizeHtml(body, sanitizeHtmlParams);
bodyHasEmoji = containsEmoji(body);
if (bodyHasEmoji) safeBody = unicodeToImage(safeBody);
}
finally {
} finally {
delete sanitizeHtmlParams.textFilter;
}
let emojiBody = false;
if (bodyHasEmoji) {
if (!opts.disableBigEmoji && bodyHasEmoji) {
EMOJI_REGEX.lastIndex = 0;
let contentBodyTrimmed = content.body !== undefined ? content.body.trim() : '';
let match = EMOJI_REGEX.exec(contentBodyTrimmed);
const contentBodyTrimmed = content.body !== undefined ? content.body.trim() : '';
const match = EMOJI_REGEX.exec(contentBodyTrimmed);
emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length;
}

View file

@ -42,13 +42,12 @@ module.exports = {
// no scaling needs to be applied
return fullHeight;
}
var widthMulti = thumbWidth / fullWidth;
var heightMulti = thumbHeight / fullHeight;
const widthMulti = thumbWidth / fullWidth;
const heightMulti = thumbHeight / fullHeight;
if (widthMulti < heightMulti) {
// width is the dominant dimension so scaling will be fixed on that
return Math.floor(widthMulti * fullHeight);
}
else {
} else {
// height is the dominant dimension so scaling will be fixed on that
return Math.floor(heightMulti * fullHeight);
}

View file

@ -59,8 +59,8 @@ export default class Login {
}
getFlows() {
var self = this;
var client = this._createTemporaryClient();
const self = this;
const client = this._createTemporaryClient();
return client.loginFlows().then(function(result) {
self._flows = result.flows;
self._currentFlowIndex = 0;
@ -77,12 +77,12 @@ export default class Login {
getCurrentFlowStep() {
// technically the flow can have multiple steps, but no one does this
// for login so we can ignore it.
var flowStep = this._flows[this._currentFlowIndex];
const flowStep = this._flows[this._currentFlowIndex];
return flowStep ? flowStep.type : null;
}
loginAsGuest() {
var client = this._createTemporaryClient();
const client = this._createTemporaryClient();
return client.registerGuest({
body: {
initial_device_display_name: this._defaultDeviceDisplayName,
@ -94,7 +94,7 @@ export default class Login {
accessToken: creds.access_token,
homeserverUrl: this._hsUrl,
identityServerUrl: this._isUrl,
guest: true
guest: true,
};
}, (error) => {
throw error;
@ -149,12 +149,12 @@ export default class Login {
identityServerUrl: self._isUrl,
userId: data.user_id,
deviceId: data.device_id,
accessToken: data.access_token
accessToken: data.access_token,
});
}, function(error) {
if (error.httpStatus === 403) {
if (self._fallbackHsUrl) {
var fbClient = Matrix.createClient({
const fbClient = Matrix.createClient({
baseUrl: self._fallbackHsUrl,
idBaseUrl: this._isUrl,
});
@ -165,7 +165,7 @@ export default class Login {
identityServerUrl: self._isUrl,
userId: data.user_id,
deviceId: data.device_id,
accessToken: data.access_token
accessToken: data.access_token,
});
}, function(fallback_error) {
// throw the original error

View file

@ -48,7 +48,7 @@ function html_if_tag_allowed(node) {
* or false if it is only a single line.
*/
function is_multi_line(node) {
var par = node;
let par = node;
while (par.parent) {
par = par.parent;
}
@ -143,7 +143,7 @@ export default class Markdown {
if (isMultiLine) this.cr();
html_if_tag_allowed.call(this, node);
if (isMultiLine) this.cr();
}
};
return renderer.render(this.parsed);
}
@ -178,7 +178,7 @@ export default class Markdown {
renderer.html_block = function(node) {
this.lit(node.literal);
if (is_multi_line(node) && node.next) this.lit('\n\n');
}
};
return renderer.render(this.parsed);
}

View file

@ -95,7 +95,7 @@ class MatrixClientPeg {
opts.pendingEventOrdering = "detached";
try {
let promise = this.matrixClient.store.startup();
const promise = this.matrixClient.store.startup();
console.log(`MatrixClientPeg: waiting for MatrixClient store to initialise`);
await promise;
} catch(err) {
@ -136,7 +136,7 @@ class MatrixClientPeg {
}
_createClient(creds: MatrixClientCreds) {
var opts = {
const opts = {
baseUrl: creds.homeserverUrl,
idBaseUrl: creds.identityServerUrl,
accessToken: creds.accessToken,
@ -153,8 +153,8 @@ class MatrixClientPeg {
this.matrixClient.setGuest(Boolean(creds.guest));
var notifTimelineSet = new EventTimelineSet(null, {
timelineSupport: true
const notifTimelineSet = new EventTimelineSet(null, {
timelineSupport: true,
});
// XXX: what is our initial pagination token?! it somehow needs to be synchronised with /sync.
notifTimelineSet.getLiveTimeline().setPaginationToken("", EventTimeline.BACKWARDS);

View file

@ -17,8 +17,8 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
const React = require('react');
const ReactDOM = require('react-dom');
import Analytics from './Analytics';
import sdk from './index';
@ -137,15 +137,15 @@ class ModalManager {
* @param {String} className CSS class to apply to the modal wrapper
*/
createDialogAsync(loader, props, className) {
var self = this;
const self = this;
const modal = {};
// never call this from onFinished() otherwise it will loop
//
// nb explicit function() rather than arrow function, to get `arguments`
var closeDialog = function() {
const closeDialog = function() {
if (props && props.onFinished) props.onFinished.apply(null, arguments);
var i = self._modals.indexOf(modal);
const i = self._modals.indexOf(modal);
if (i >= 0) {
self._modals.splice(i, 1);
}
@ -160,7 +160,7 @@ class ModalManager {
// property set here so you can't close the dialog from a button click!
modal.elem = (
<AsyncWrapper key={modalCount} loader={loader} {...props}
onFinished={closeDialog}/>
onFinished={closeDialog} />
);
modal.onFinished = props ? props.onFinished : null;
modal.className = className;
@ -191,13 +191,13 @@ class ModalManager {
return;
}
var modal = this._modals[0];
var dialog = (
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '') }>
const modal = this._modals[0];
const dialog = (
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '')}>
<div className="mx_Dialog">
{modal.elem}
{ modal.elem }
</div>
<div className="mx_Dialog_background" onClick={ this.closeAll }></div>
<div className="mx_Dialog_background" onClick={this.closeAll}></div>
</div>
);

View file

@ -81,7 +81,7 @@ const Notifier = {
}
const avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
ev.sender, 40, 40, 'crop'
ev.sender, 40, 40, 'crop',
) : null;
const notif = plaf.displayNotification(title, msg, avatarUrl, room);
@ -303,7 +303,7 @@ const Notifier = {
this._playAudioNotification(ev, room);
}
}
}
},
};
if (!global.mxNotifier) {

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
var dis = require("./dispatcher");
const MatrixClientPeg = require("./MatrixClientPeg");
const dis = require("./dispatcher");
// Time in ms after that a user is considered as unavailable/away
var UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
var PRESENCE_STATES = ["online", "offline", "unavailable"];
const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
const PRESENCE_STATES = ["online", "offline", "unavailable"];
class Presence {
@ -71,14 +71,14 @@ class Presence {
if (!this.running) {
return;
}
var old_state = this.state;
const old_state = this.state;
this.state = newState;
if (MatrixClientPeg.get().isGuest()) {
return; // don't try to set presence when a guest; it won't work.
}
var self = this;
const self = this;
MatrixClientPeg.get().setPresence(this.state).done(function() {
console.log("Presence: %s", newState);
}, function(err) {
@ -104,7 +104,7 @@ class Presence {
* @private
*/
_resetTimer() {
var self = this;
const self = this;
this.setState("online");
// Re-arm the timer
clearTimeout(this.timer);

View file

@ -44,9 +44,9 @@ export const contentStateToHTML = (contentState: ContentState) => {
return stateToHTML(contentState, {
inlineStyles: {
UNDERLINE: {
element: 'u'
}
}
element: 'u',
},
},
});
};
@ -59,7 +59,7 @@ function unicodeToEmojiUri(str) {
let replaceWith, unicode, alt;
if ((!emojione.unicodeAlt) || (emojione.sprites)) {
// if we are using the shortname as the alt tag then we need a reversed array to map unicode code point to shortnames
let mappedUnicode = emojione.mapUnicodeToShort();
const mappedUnicode = emojione.mapUnicodeToShort();
}
str = str.replace(emojione.regUnicode, function(unicodeChar) {
@ -67,8 +67,14 @@ function unicodeToEmojiUri(str) {
// if the unicodeChar doesnt exist just return the entire match
return unicodeChar;
} else {
// Remove variant selector VS16 (explicitly emoji) as it is unnecessary and leads to an incorrect URL below
if(unicodeChar.length == 2 && unicodeChar[1] == '\ufe0f') {
unicodeChar = unicodeChar[0];
}
// get the unicode codepoint from the actual char
unicode = emojione.jsEscapeMap[unicodeChar];
return emojione.imagePathSVG+unicode+'.svg'+emojione.cacheBustParam;
}
});
@ -90,14 +96,14 @@ function findWithRegex(regex, contentBlock: ContentBlock, callback: (start: numb
}
// Workaround for https://github.com/facebook/draft-js/issues/414
let emojiDecorator = {
const emojiDecorator = {
strategy: (contentState, contentBlock, callback) => {
findWithRegex(EMOJI_REGEX, contentBlock, callback);
},
component: (props) => {
let uri = unicodeToEmojiUri(props.children[0].props.text);
let shortname = emojione.toShort(props.children[0].props.text);
let style = {
const uri = unicodeToEmojiUri(props.children[0].props.text);
const shortname = emojione.toShort(props.children[0].props.text);
const style = {
display: 'inline-block',
width: '1em',
maxHeight: '1em',
@ -106,7 +112,7 @@ let emojiDecorator = {
backgroundPosition: 'center center',
overflow: 'hidden',
};
return (<span title={shortname} style={style}><span style={{opacity: 0}}>{props.children}</span></span>);
return (<span title={shortname} style={style}><span style={{opacity: 0}}>{ props.children }</span></span>);
},
};
@ -118,16 +124,16 @@ export function getScopedRTDecorators(scope: any): CompositeDecorator {
}
export function getScopedMDDecorators(scope: any): CompositeDecorator {
let markdownDecorators = ['HR', 'BOLD', 'ITALIC', 'CODE', 'STRIKETHROUGH'].map(
const markdownDecorators = ['HR', 'BOLD', 'ITALIC', 'CODE', 'STRIKETHROUGH'].map(
(style) => ({
strategy: (contentState, contentBlock, callback) => {
return findWithRegex(MARKDOWN_REGEX[style], contentBlock, callback);
},
component: (props) => (
<span className={"mx_MarkdownElement mx_Markdown_" + style}>
{props.children}
{ props.children }
</span>
)
),
}));
markdownDecorators.push({
@ -136,9 +142,9 @@ export function getScopedMDDecorators(scope: any): CompositeDecorator {
},
component: (props) => (
<a href="#" className="mx_MarkdownElement mx_Markdown_LINK">
{props.children}
{ props.children }
</a>
)
),
});
// markdownDecorators.push(emojiDecorator);
// TODO Consider renabling "syntax highlighting" when we can do it properly
@ -161,7 +167,7 @@ export function modifyText(contentState: ContentState, rangeToReplace: Selection
for (let currentKey = startKey;
currentKey && currentKey !== endKey;
currentKey = contentState.getKeyAfter(currentKey)) {
let blockText = getText(currentKey);
const blockText = getText(currentKey);
text += blockText.substring(startOffset, blockText.length);
// from now on, we'll take whole blocks
@ -182,7 +188,7 @@ export function modifyText(contentState: ContentState, rangeToReplace: Selection
export function selectionStateToTextOffsets(selectionState: SelectionState,
contentBlocks: Array<ContentBlock>): {start: number, end: number} {
let offset = 0, start = 0, end = 0;
for (let block of contentBlocks) {
for (const block of contentBlocks) {
if (selectionState.getStartKey() === block.getKey()) {
start = offset + selectionState.getStartOffset();
}
@ -259,7 +265,7 @@ export function attachImmutableEntitiesToEmoji(editorState: EditorState): Editor
.set('focusOffset', end);
const emojiText = plainText.substring(start, end);
newContentState = newContentState.createEntity(
'emoji', 'IMMUTABLE', { emojiUnicode: emojiText }
'emoji', 'IMMUTABLE', { emojiUnicode: emojiText },
);
const entityKey = newContentState.getLastCreatedEntityKey();
newContentState = Modifier.replaceText(

View file

@ -62,8 +62,7 @@ export function isConfCallRoom(room, me, conferenceHandler) {
export function looksLikeDirectMessageRoom(room, me) {
if (me.membership == "join" || me.membership === "ban" ||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey()))
{
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
// Used to split rooms via tags
const tagNames = Object.keys(room.tags);
// Used for 1:1 direct chats

View file

@ -15,10 +15,10 @@ limitations under the License.
*/
import Promise from 'bluebird';
var request = require('browser-request');
const request = require('browser-request');
var SdkConfig = require('./SdkConfig');
var MatrixClientPeg = require('./MatrixClientPeg');
const SdkConfig = require('./SdkConfig');
const MatrixClientPeg = require('./MatrixClientPeg');
class ScalarAuthClient {
@ -38,7 +38,7 @@ class ScalarAuthClient {
// Returns a scalar_token string
getScalarToken() {
var tok = window.localStorage.getItem("mx_scalar_token");
const tok = window.localStorage.getItem("mx_scalar_token");
if (tok) return Promise.resolve(tok);
// No saved token, so do the dance to get one. First, we
@ -53,9 +53,9 @@ class ScalarAuthClient {
}
exchangeForScalarToken(openid_token_object) {
var defer = Promise.defer();
const defer = Promise.defer();
var scalar_rest_url = SdkConfig.get().integrations_rest_url;
const scalar_rest_url = SdkConfig.get().integrations_rest_url;
request({
method: 'POST',
uri: scalar_rest_url+'/register',
@ -77,7 +77,7 @@ class ScalarAuthClient {
}
getScalarInterfaceUrlForRoom(roomId, screen, id) {
var url = SdkConfig.get().integrations_ui_url;
let url = SdkConfig.get().integrations_ui_url;
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
url += "&room_id=" + encodeURIComponent(roomId);
if (id) {

View file

@ -356,12 +356,12 @@ function getWidgets(event, roomId) {
}
const stateEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
// Only return widgets which have required fields
let widgetStateEvents = [];
const widgetStateEvents = [];
stateEvents.forEach((ev) => {
if (ev.getContent().type && ev.getContent().url) {
widgetStateEvents.push(ev.event); // return the raw event
}
})
});
sendResponse(event, widgetStateEvents);
}
@ -376,7 +376,7 @@ function setPlumbingState(event, roomId, status) {
sendError(event, _t('You need to be logged in.'));
return;
}
client.sendStateEvent(roomId, "m.room.plumbing", { status : status }).done(() => {
client.sendStateEvent(roomId, "m.room.plumbing", { status: status }).done(() => {
sendResponse(event, {
success: true,
});
@ -415,11 +415,11 @@ function setBotPower(event, roomId, userId, level) {
}
client.getStateEvent(roomId, "m.room.power_levels", "").then((powerLevels) => {
let powerEvent = new MatrixEvent(
const powerEvent = new MatrixEvent(
{
type: "m.room.power_levels",
content: powerLevels,
}
},
);
client.setPowerLevel(roomId, userId, level, powerEvent).done(() => {
@ -485,8 +485,7 @@ function canSendEvent(event, roomId) {
let canSend = false;
if (isState) {
canSend = room.currentState.maySendStateEvent(evType, me);
}
else {
} else {
canSend = room.currentState.maySendEvent(evType, me);
}
@ -517,8 +516,8 @@ function returnStateEvent(event, roomId, eventType, stateKey) {
sendResponse(event, stateEvent.getContent());
}
var currentRoomId = null;
var currentRoomAlias = null;
let currentRoomId = null;
let currentRoomAlias = null;
// Listen for when a room is viewed
dis.register(onAction);
@ -542,7 +541,7 @@ const onMessage = function(event) {
//
// All strings start with the empty string, so for sanity return if the length
// of the event origin is 0.
let url = SdkConfig.get().integrations_ui_url;
const url = SdkConfig.get().integrations_ui_url;
if (event.origin.length === 0 || !url.startsWith(event.origin) || !event.data.action) {
return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise
}
@ -647,7 +646,7 @@ module.exports = {
// Make an error so we get a stack trace
const e = new Error(
"ScalarMessaging: mismatched startListening / stopListening detected." +
" Negative count"
" Negative count",
);
console.error(e);
}

View file

@ -243,7 +243,7 @@ function textForPowerEvent(event) {
if (to !== from) {
diff.push(
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
userId: userId,
userId,
fromPowerLevel: Roles.textualPowerLevel(from, userDefault),
toPowerLevel: Roles.textualPowerLevel(to, userDefault),
}),
@ -254,7 +254,7 @@ function textForPowerEvent(event) {
return '';
}
return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
senderName: senderName,
senderName,
powerLevelDiffText: diff.join(", "),
});
}
@ -296,12 +296,15 @@ function textForWidgetEvent(event) {
const handlers = {
'm.room.message': textForMessageEvent,
'm.room.name': textForRoomNameEvent,
'm.room.topic': textForTopicEvent,
'm.room.member': textForMemberEvent,
'm.call.invite': textForCallInviteEvent,
'm.call.answer': textForCallAnswerEvent,
'm.call.hangup': textForCallHangupEvent,
};
const stateHandlers = {
'm.room.name': textForRoomNameEvent,
'm.room.topic': textForTopicEvent,
'm.room.member': textForMemberEvent,
'm.room.third_party_invite': textForThreePidInviteEvent,
'm.room.history_visibility': textForHistoryVisibilityEvent,
'm.room.encryption': textForEncryptionEvent,
@ -313,8 +316,8 @@ const handlers = {
module.exports = {
textForEvent: function(ev) {
const hdlr = handlers[ev.getType()];
if (!hdlr) return '';
return hdlr(ev);
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
if (handler) return handler(ev);
return '';
},
};

View file

@ -18,10 +18,10 @@ limitations under the License.
// module.exports otherwise this will break when included by both
// react-sdk and apps layered on top.
var DEBUG = 0;
const DEBUG = 0;
// The colour keys to be replaced as referred to in CSS
var keyRgb = [
const keyRgb = [
"rgb(118, 207, 166)", // Vector Green
"rgb(234, 245, 240)", // Vector Light Green
"rgb(211, 239, 225)", // BottomLeftMenu overlay (20% Vector Green)
@ -35,7 +35,7 @@ var keyRgb = [
// x = (255 - 234) / (255 - 118) = 0.16
// The colour keys to be replaced as referred to in SVGs
var keyHex = [
const keyHex = [
"#76CFA6", // Vector Green
"#EAF5F0", // Vector Light Green
"#D3EFE1", // BottomLeftMenu overlay (20% Vector Green overlaid on Vector Light Green)
@ -44,14 +44,14 @@ var keyHex = [
// cache of our replacement colours
// defaults to our keys.
var colors = [
const colors = [
keyHex[0],
keyHex[1],
keyHex[2],
keyHex[3],
];
var cssFixups = [
const cssFixups = [
// {
// style: a style object that should be fixed up taken from a stylesheet
// attr: name of the attribute to be clobbered, e.g. 'color'
@ -60,7 +60,7 @@ var cssFixups = [
];
// CSS attributes to be fixed up
var cssAttrs = [
const cssAttrs = [
"color",
"backgroundColor",
"borderColor",
@ -69,17 +69,17 @@ var cssAttrs = [
"borderLeftColor",
];
var svgAttrs = [
const svgAttrs = [
"fill",
"stroke",
];
var cached = false;
let cached = false;
function calcCssFixups() {
if (DEBUG) console.log("calcSvgFixups start");
for (var i = 0; i < document.styleSheets.length; i++) {
var ss = document.styleSheets[i];
for (let i = 0; i < document.styleSheets.length; i++) {
const ss = document.styleSheets[i];
if (!ss) continue; // well done safari >:(
// Chromium apparently sometimes returns null here; unsure why.
// see $14534907369972FRXBx:matrix.org in HQ
@ -104,12 +104,12 @@ function calcCssFixups() {
if (ss.href && !ss.href.match(/\/bundle.*\.css$/)) continue;
if (!ss.cssRules) continue;
for (var j = 0; j < ss.cssRules.length; j++) {
var rule = ss.cssRules[j];
for (let j = 0; j < ss.cssRules.length; j++) {
const rule = ss.cssRules[j];
if (!rule.style) continue;
for (var k = 0; k < cssAttrs.length; k++) {
var attr = cssAttrs[k];
for (var l = 0; l < keyRgb.length; l++) {
for (let k = 0; k < cssAttrs.length; k++) {
const attr = cssAttrs[k];
for (let l = 0; l < keyRgb.length; l++) {
if (rule.style[attr] === keyRgb[l]) {
cssFixups.push({
style: rule.style,
@ -126,8 +126,8 @@ function calcCssFixups() {
function applyCssFixups() {
if (DEBUG) console.log("applyCssFixups start");
for (var i = 0; i < cssFixups.length; i++) {
var cssFixup = cssFixups[i];
for (let i = 0; i < cssFixups.length; i++) {
const cssFixup = cssFixups[i];
cssFixup.style[cssFixup.attr] = colors[cssFixup.index];
}
if (DEBUG) console.log("applyCssFixups end");
@ -140,15 +140,15 @@ function hexToRgb(color) {
color[1] + color[1] +
color[2] + color[2];
}
var val = parseInt(color, 16);
var r = (val >> 16) & 255;
var g = (val >> 8) & 255;
var b = val & 255;
const val = parseInt(color, 16);
const r = (val >> 16) & 255;
const g = (val >> 8) & 255;
const b = val & 255;
return [r, g, b];
}
function rgbToHex(rgb) {
var val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
const val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
return '#' + (0x1000000 + val).toString(16).slice(1);
}
@ -167,12 +167,11 @@ module.exports = {
*
* @param {Function} tintable Function to call when the tint changes.
*/
registerTintable : function(tintable) {
registerTintable: function(tintable) {
tintables.push(tintable);
},
tint: function(primaryColor, secondaryColor, tertiaryColor) {
if (!cached) {
calcCssFixups();
cached = true;
@ -185,7 +184,7 @@ module.exports = {
if (!secondaryColor) {
const x = 0.16; // average weighting factor calculated from vector green & light green
var rgb = hexToRgb(primaryColor);
const rgb = hexToRgb(primaryColor);
rgb[0] = x * rgb[0] + (1 - x) * 255;
rgb[1] = x * rgb[1] + (1 - x) * 255;
rgb[2] = x * rgb[2] + (1 - x) * 255;
@ -194,8 +193,8 @@ module.exports = {
if (!tertiaryColor) {
const x = 0.19;
var rgb1 = hexToRgb(primaryColor);
var rgb2 = hexToRgb(secondaryColor);
const rgb1 = hexToRgb(primaryColor);
const rgb2 = hexToRgb(secondaryColor);
rgb1[0] = x * rgb1[0] + (1 - x) * rgb2[0];
rgb1[1] = x * rgb1[1] + (1 - x) * rgb2[1];
rgb1[2] = x * rgb1[2] + (1 - x) * rgb2[2];
@ -204,8 +203,7 @@ module.exports = {
if (colors[0] === primaryColor &&
colors[1] === secondaryColor &&
colors[2] === tertiaryColor)
{
colors[2] === tertiaryColor) {
return;
}
@ -248,14 +246,13 @@ module.exports = {
// key colour; cache the element and apply.
if (DEBUG) console.log("calcSvgFixups start for " + svgs);
var fixups = [];
for (var i = 0; i < svgs.length; i++) {
const fixups = [];
for (let i = 0; i < svgs.length; i++) {
var svgDoc;
try {
svgDoc = svgs[i].contentDocument;
}
catch(e) {
var msg = 'Failed to get svg.contentDocument of ' + svgs[i].toString();
} catch(e) {
let msg = 'Failed to get svg.contentDocument of ' + svgs[i].toString();
if (e.message) {
msg += e.message;
}
@ -265,12 +262,12 @@ module.exports = {
console.error(e);
}
if (!svgDoc) continue;
var tags = svgDoc.getElementsByTagName("*");
for (var j = 0; j < tags.length; j++) {
var tag = tags[j];
for (var k = 0; k < svgAttrs.length; k++) {
var attr = svgAttrs[k];
for (var l = 0; l < keyHex.length; l++) {
const tags = svgDoc.getElementsByTagName("*");
for (let j = 0; j < tags.length; j++) {
const tag = tags[j];
for (let k = 0; k < svgAttrs.length; k++) {
const attr = svgAttrs[k];
for (let l = 0; l < keyHex.length; l++) {
if (tag.getAttribute(attr) && tag.getAttribute(attr).toUpperCase() === keyHex[l]) {
fixups.push({
node: tag,
@ -289,10 +286,10 @@ module.exports = {
applySvgFixups: function(fixups) {
if (DEBUG) console.log("applySvgFixups start for " + fixups);
for (var i = 0; i < fixups.length; i++) {
var svgFixup = fixups[i];
for (let i = 0; i < fixups.length; i++) {
const svgFixup = fixups[i];
svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]);
}
if (DEBUG) console.log("applySvgFixups end");
}
},
};

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var MatrixClientPeg = require('./MatrixClientPeg');
const MatrixClientPeg = require('./MatrixClientPeg');
import UserSettingsStore from './UserSettingsStore';
import shouldHideEvent from './shouldHideEvent';
var sdk = require('./index');
const sdk = require('./index');
module.exports = {
/**
@ -34,17 +34,17 @@ module.exports = {
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
return false;
}
var EventTile = sdk.getComponent('rooms.EventTile');
const EventTile = sdk.getComponent('rooms.EventTile');
return EventTile.haveTileForEvent(ev);
},
doesRoomHaveUnreadMessages: function(room) {
var myUserId = MatrixClientPeg.get().credentials.userId;
const myUserId = MatrixClientPeg.get().credentials.userId;
// get the most recent read receipt sent by our account.
// N.B. this is NOT a read marker (RM, aka "read up to marker"),
// despite the name of the method :((
var readUpToId = room.getEventReadUpTo(myUserId);
const readUpToId = room.getEventReadUpTo(myUserId);
// as we don't send RRs for our own messages, make sure we special case that
// if *we* sent the last message into the room, we consider it not unread!
@ -54,8 +54,7 @@ module.exports = {
// https://github.com/vector-im/riot-web/issues/3363
if (room.timeline.length &&
room.timeline[room.timeline.length - 1].sender &&
room.timeline[room.timeline.length - 1].sender.userId === myUserId)
{
room.timeline[room.timeline.length - 1].sender.userId === myUserId) {
return false;
}
@ -67,8 +66,8 @@ module.exports = {
const syncedSettings = UserSettingsStore.getSyncedSettings();
// Loop through messages, starting with the most recent...
for (var i = room.timeline.length - 1; i >= 0; --i) {
var ev = room.timeline[i];
for (let i = room.timeline.length - 1; i >= 0; --i) {
const ev = room.timeline[i];
if (ev.getId() == readUpToId) {
// If we've read up to this event, there's nothing more recents
@ -86,5 +85,5 @@ module.exports = {
// is unread on the theory that false positives are better than
// false negatives here.
return true;
}
},
};

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,33 +18,51 @@ limitations under the License.
import Promise from 'bluebird';
import MatrixClientPeg from './MatrixClientPeg';
import Notifier from './Notifier';
import { _t } from './languageHandler';
import { _t, _td } from './languageHandler';
import SdkConfig from './SdkConfig';
/*
* TODO: Make things use this. This is all WIP - see UserSettings.js for usage.
*/
const FEATURES = [
{
id: 'feature_groups',
name: _td("Groups"),
},
];
export default {
LABS_FEATURES: [
{
name: "-",
id: 'matrix_apps',
default: true,
getLabsFeatures() {
const featuresConfig = SdkConfig.get()['features'] || {};
// XXX: Always use default, ignore localStorage and remove from labs
override: true,
},
{
name: "-",
id: 'feature_groups',
default: false,
},
],
// The old flag: honourned for backwards compat
const enableLabs = SdkConfig.get()['enableLabs'];
// horrible but it works. The locality makes this somewhat more palatable.
doTranslations: function() {
this.LABS_FEATURES[0].name = _t("Matrix Apps");
this.LABS_FEATURES[1].name = _t("Groups");
let labsFeatures;
if (enableLabs) {
labsFeatures = FEATURES;
} else {
labsFeatures = FEATURES.filter((f) => {
const sdkConfigValue = featuresConfig[f.id];
if (sdkConfigValue === 'labs') {
return true;
}
});
}
return labsFeatures.map((f) => {
return f.id;
});
},
translatedNameForFeature(featureId) {
const feature = FEATURES.filter((f) => {
return f.id === featureId;
})[0];
if (feature === undefined) return null;
return _t(feature.name);
},
loadProfileInfo: function() {
@ -180,33 +199,33 @@ export default {
localStorage.setItem('mx_local_settings', JSON.stringify(settings));
},
getFeatureById(feature: string) {
for (let i = 0; i < this.LABS_FEATURES.length; i++) {
const f = this.LABS_FEATURES[i];
if (f.id === feature) {
return f;
}
}
return null;
},
isFeatureEnabled: function(featureId: string): boolean {
// Disable labs for guests.
if (MatrixClientPeg.get().isGuest()) return false;
const featuresConfig = SdkConfig.get()['features'];
const feature = this.getFeatureById(featureId);
if (!feature) {
console.warn(`Unknown feature "${featureId}"`);
// The old flag: honourned for backwards compat
const enableLabs = SdkConfig.get()['enableLabs'];
let sdkConfigValue = enableLabs ? 'labs' : 'disable';
if (featuresConfig && featuresConfig[featureId] !== undefined) {
sdkConfigValue = featuresConfig[featureId];
}
if (sdkConfigValue === 'enable') {
return true;
} else if (sdkConfigValue === 'disable') {
return false;
} else if (sdkConfigValue === 'labs') {
if (!MatrixClientPeg.get().isGuest()) {
// Make it explicit that guests get the defaults (although they shouldn't
// have been able to ever toggle the flags anyway)
const userValue = localStorage.getItem(`mx_labs_feature_${featureId}`);
return userValue === 'true';
}
return false;
} else {
console.warn(`Unknown features config for ${featureId}: ${sdkConfigValue}`);
return false;
}
// Return the default if this feature has an override to be the default value or
// if the feature has never been toggled and is therefore not in localStorage
if (Object.keys(feature).includes('override') ||
localStorage.getItem(`mx_labs_feature_${featureId}`) === null
) {
return feature.default;
}
return localStorage.getItem(`mx_labs_feature_${featureId}`) === 'true';
},
setFeatureEnabled: function(featureId: string, enabled: boolean) {

View file

@ -1,6 +1,6 @@
var React = require('react');
var ReactDom = require('react-dom');
var Velocity = require('velocity-vector');
const React = require('react');
const ReactDom = require('react-dom');
const Velocity = require('velocity-vector');
/**
* The Velociraptor contains components and animates transitions with velocity.
@ -46,13 +46,13 @@ module.exports = React.createClass({
* update `this.children` according to the new list of children given
*/
_updateChildren: function(newChildren) {
var self = this;
var oldChildren = this.children || {};
const self = this;
const oldChildren = this.children || {};
this.children = {};
React.Children.toArray(newChildren).forEach(function(c) {
if (oldChildren[c.key]) {
var old = oldChildren[c.key];
var oldNode = ReactDom.findDOMNode(self.nodes[old.key]);
const old = oldChildren[c.key];
const oldNode = ReactDom.findDOMNode(self.nodes[old.key]);
if (oldNode && oldNode.style.left != c.props.style.left) {
Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() {
@ -71,18 +71,18 @@ module.exports = React.createClass({
} else {
// new element. If we have a startStyle, use that as the style and go through
// the enter animations
var newProps = {};
var restingStyle = c.props.style;
const newProps = {};
const restingStyle = c.props.style;
var startStyles = self.props.startStyles;
const startStyles = self.props.startStyles;
if (startStyles.length > 0) {
var startStyle = startStyles[0];
const startStyle = startStyles[0];
newProps.style = startStyle;
// console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
}
newProps.ref = (n => self._collectNode(
c.key, n, restingStyle
newProps.ref = ((n) => self._collectNode(
c.key, n, restingStyle,
));
self.children[c.key] = React.cloneElement(c, newProps);
@ -103,8 +103,8 @@ module.exports = React.createClass({
this.nodes[k] === undefined &&
this.props.startStyles.length > 0
) {
var startStyles = this.props.startStyles;
var transitionOpts = this.props.enterTransitionOpts;
const startStyles = this.props.startStyles;
const transitionOpts = this.props.enterTransitionOpts;
const domNode = ReactDom.findDOMNode(node);
// start from startStyle 1: 0 is the one we gave it
// to start with, so now we animate 1 etc.
@ -154,7 +154,7 @@ module.exports = React.createClass({
render: function() {
return (
<span>
{Object.values(this.children)}
{ Object.values(this.children) }
</span>
);
},

View file

@ -1,9 +1,9 @@
var Velocity = require('velocity-vector');
const Velocity = require('velocity-vector');
// courtesy of https://github.com/julianshapiro/velocity/issues/283
// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
function bounce( p ) {
var pow2,
let pow2,
bounce = 4;
while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {

View file

@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
const MatrixClientPeg = require("./MatrixClientPeg");
import { _t } from './languageHandler';
module.exports = {
usersTypingApartFromMeAndIgnored: function(room) {
return this.usersTyping(
room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers())
room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers()),
);
},
usersTypingApartFromMe: function(room) {
return this.usersTyping(
room, [MatrixClientPeg.get().credentials.userId]
room, [MatrixClientPeg.get().credentials.userId],
);
},
@ -35,15 +35,15 @@ module.exports = {
* to exclude, return a list of user objects who are typing.
*/
usersTyping: function(room, exclude) {
var whoIsTyping = [];
const whoIsTyping = [];
if (exclude === undefined) {
exclude = [];
}
var memberKeys = Object.keys(room.currentState.members);
for (var i = 0; i < memberKeys.length; ++i) {
var userId = memberKeys[i];
const memberKeys = Object.keys(room.currentState.members);
for (let i = 0; i < memberKeys.length; ++i) {
const userId = memberKeys[i];
if (room.currentState.members[userId].typing) {
if (exclude.indexOf(userId) == -1) {
@ -76,5 +76,5 @@ module.exports = {
const lastPerson = names.pop();
return _t('%(names)s and %(lastPerson)s are typing', {names: names.join(', '), lastPerson: lastPerson});
}
}
},
};

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require("react");
const React = require("react");
import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
const sdk = require('../../../index');
const MatrixClientPeg = require("../../../MatrixClientPeg");
module.exports = React.createClass({
displayName: 'EncryptedEventDialog',
@ -33,7 +33,7 @@ module.exports = React.createClass({
componentWillMount: function() {
this._unmounted = false;
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
// first try to load the device from our store.
//
@ -60,7 +60,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
this._unmounted = true;
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
}
@ -89,12 +89,12 @@ module.exports = React.createClass({
},
_renderDeviceInfo: function() {
var device = this.state.device;
const device = this.state.device;
if (!device) {
return (<i>{ _t('unknown device') }</i>);
}
var verificationStatus = (<b>{ _t('NOT verified') }</b>);
let verificationStatus = (<b>{ _t('NOT verified') }</b>);
if (device.isBlocked()) {
verificationStatus = (<b>{ _t('Blacklisted') }</b>);
} else if (device.isVerified()) {
@ -118,7 +118,7 @@ module.exports = React.createClass({
</tr>
<tr>
<td>{ _t('Ed25519 fingerprint') }</td>
<td><code>{device.getFingerprint()}</code></td>
<td><code>{ device.getFingerprint() }</code></td>
</tr>
</tbody>
</table>
@ -126,7 +126,7 @@ module.exports = React.createClass({
},
_renderEventInfo: function() {
var event = this.props.event;
const event = this.props.event;
return (
<table>
@ -165,36 +165,36 @@ module.exports = React.createClass({
},
render: function() {
var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
const DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
var buttons = null;
let buttons = null;
if (this.state.device) {
buttons = (
<DeviceVerifyButtons device={ this.state.device }
userId={ this.props.event.getSender() }
<DeviceVerifyButtons device={this.state.device}
userId={this.props.event.getSender()}
/>
);
}
return (
<div className="mx_EncryptedEventDialog" onKeyDown={ this.onKeyDown }>
<div className="mx_EncryptedEventDialog" onKeyDown={this.onKeyDown}>
<div className="mx_Dialog_title">
{ _t('End-to-end encryption information') }
</div>
<div className="mx_Dialog_content">
<h4>{ _t('Event information') }</h4>
{this._renderEventInfo()}
{ this._renderEventInfo() }
<h4>{ _t('Sender device information') }</h4>
{this._renderDeviceInfo()}
{ this._renderDeviceInfo() }
</div>
<div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" onClick={ this.props.onFinished } autoFocus={ true }>
<button className="mx_Dialog_primary" onClick={this.props.onFinished} autoFocus={true}>
{ _t('OK') }
</button>
{buttons}
{ buttons }
</div>
</div>
);
}
},
});

View file

@ -45,7 +45,7 @@ const PROVIDERS = [
EmojiProvider,
CommandProvider,
DuckDuckGoProvider,
].map(completer => completer.getInstance());
].map((completer) => completer.getInstance());
// Providers will get rejected if they take longer than this.
const PROVIDER_COMPLETION_TIMEOUT = 3000;

View file

@ -16,7 +16,7 @@ limitations under the License.
*/
import React from 'react';
import { _t } from '../languageHandler';
import { _t, _td } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import FuzzyMatcher from './FuzzyMatcher';
import {TextualCompletion} from './Components';
@ -27,82 +27,82 @@ const COMMANDS = [
{
command: '/me',
args: '<message>',
description: 'Displays action',
description: _td('Displays action'),
},
{
command: '/ban',
args: '<user-id> [reason]',
description: 'Bans user with given id',
description: _td('Bans user with given id'),
},
{
command: '/unban',
args: '<user-id>',
description: 'Unbans user with given id',
description: _td('Unbans user with given id'),
},
{
command: '/op',
args: '<user-id> [<power-level>]',
description: 'Define the power level of a user',
description: _td('Define the power level of a user'),
},
{
command: '/deop',
args: '<user-id>',
description: 'Deops user with given id',
description: _td('Deops user with given id'),
},
{
command: '/invite',
args: '<user-id>',
description: 'Invites user with given id to current room',
description: _td('Invites user with given id to current room'),
},
{
command: '/join',
args: '<room-alias>',
description: 'Joins room with given alias',
description: _td('Joins room with given alias'),
},
{
command: '/part',
args: '[<room-alias>]',
description: 'Leave room',
description: _td('Leave room'),
},
{
command: '/topic',
args: '<topic>',
description: 'Sets the room topic',
description: _td('Sets the room topic'),
},
{
command: '/kick',
args: '<user-id> [reason]',
description: 'Kicks user with given id',
description: _td('Kicks user with given id'),
},
{
command: '/nick',
args: '<display-name>',
description: 'Changes your display nickname',
description: _td('Changes your display nickname'),
},
{
command: '/ddg',
args: '<query>',
description: 'Searches DuckDuckGo for results',
description: _td('Searches DuckDuckGo for results'),
},
{
command: '/tint',
args: '<color1> [<color2>]',
description: 'Changes colour scheme of current room',
description: _td('Changes colour scheme of current room'),
},
{
command: '/verify',
args: '<user-id> <device-id> <device-signing-key>',
description: 'Verifies a user, device, and pubkey tuple',
description: _td('Verifies a user, device, and pubkey tuple'),
},
{
command: '/ignore',
args: '<user-id>',
description: 'Ignores a user, hiding their messages from you',
description: _td('Ignores a user, hiding their messages from you'),
},
{
command: '/unignore',
args: '<user-id>',
description: 'Stops ignoring a user, showing their messages going forward',
description: _td('Stops ignoring a user, showing their messages going forward'),
},
// Omitting `/markdown` as it only seems to apply to OldComposer
];

View file

@ -30,13 +30,13 @@ export class TextualCompletion extends React.Component {
subtitle,
description,
className,
...restProps,
...restProps
} = this.props;
return (
<div className={classNames('mx_Autocomplete_Completion_block', className)} {...restProps}>
<span className="mx_Autocomplete_Completion_title">{title}</span>
<span className="mx_Autocomplete_Completion_subtitle">{subtitle}</span>
<span className="mx_Autocomplete_Completion_description">{description}</span>
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
<span className="mx_Autocomplete_Completion_description">{ description }</span>
</div>
);
}
@ -56,14 +56,14 @@ export class PillCompletion extends React.Component {
description,
initialComponent,
className,
...restProps,
...restProps
} = this.props;
return (
<div className={classNames('mx_Autocomplete_Completion_pill', className)} {...restProps}>
{initialComponent}
<span className="mx_Autocomplete_Completion_title">{title}</span>
<span className="mx_Autocomplete_Completion_subtitle">{subtitle}</span>
<span className="mx_Autocomplete_Completion_description">{description}</span>
{ initialComponent }
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
<span className="mx_Autocomplete_Completion_description">{ description }</span>
</div>
);
}

View file

@ -38,7 +38,7 @@ export default class DuckDuckGoProvider extends AutocompleteProvider {
}
async getCompletions(query: string, selection: {start: number, end: number}) {
let {command, range} = this.getCurrentCommand(query, selection);
const {command, range} = this.getCurrentCommand(query, selection);
if (!query || !command) {
return [];
}
@ -47,7 +47,7 @@ export default class DuckDuckGoProvider extends AutocompleteProvider {
method: 'GET',
});
const json = await response.json();
let results = json.Results.map(result => {
const results = json.Results.map((result) => {
return {
completion: result.Text,
component: (
@ -105,7 +105,7 @@ export default class DuckDuckGoProvider extends AutocompleteProvider {
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_block">
{completions}
{ completions }
</div>;
}
}

View file

@ -138,7 +138,7 @@ export default class EmojiProvider extends AutocompleteProvider {
return {
completion: unicode,
component: (
<PillCompletion title={shortname} initialComponent={<EmojiText style={{maxWidth: '1em'}}>{unicode}</EmojiText>} />
<PillCompletion title={shortname} initialComponent={<EmojiText style={{maxWidth: '1em'}}>{ unicode }</EmojiText>} />
),
range,
};
@ -152,14 +152,13 @@ export default class EmojiProvider extends AutocompleteProvider {
}
static getInstance() {
if (instance == null)
{instance = new EmojiProvider();}
if (instance == null) {instance = new EmojiProvider();}
return instance;
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill">
{completions}
{ completions }
</div>;
}
}

View file

@ -59,7 +59,7 @@ export default class UserProvider extends AutocompleteProvider {
if (this.users === null) this._makeUsers();
let completions = [];
let {command, range} = this.getCurrentCommand(query, selection, force);
const {command, range} = this.getCurrentCommand(query, selection, force);
if (command) {
completions = this.matcher.match(command[0]).map((user) => {
const displayName = (user.name || user.userId || '').replace(' (IRC)', ''); // FIXME when groups are done
@ -71,7 +71,7 @@ export default class UserProvider extends AutocompleteProvider {
href: 'https://matrix.to/#/' + user.userId,
component: (
<PillCompletion
initialComponent={<MemberAvatar member={user} width={24} height={24}/>}
initialComponent={<MemberAvatar member={user} width={24} height={24} />}
title={displayName}
description={user.userId} />
),
@ -132,7 +132,7 @@ export default class UserProvider extends AutocompleteProvider {
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate">
{completions}
{ completions }
</div>;
}

View file

@ -17,9 +17,9 @@ limitations under the License.
'use strict';
var classNames = require('classnames');
var React = require('react');
var ReactDOM = require('react-dom');
const classNames = require('classnames');
const React = require('react');
const ReactDOM = require('react-dom');
// Shamelessly ripped off Modal.js. There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
@ -36,7 +36,7 @@ module.exports = {
},
getOrCreateContainer: function() {
var container = document.getElementById(this.ContextualMenuContainerId);
let container = document.getElementById(this.ContextualMenuContainerId);
if (!container) {
container = document.createElement("div");
@ -48,9 +48,9 @@ module.exports = {
},
createMenu: function(Element, props) {
var self = this;
const self = this;
var closeMenu = function() {
const closeMenu = function() {
ReactDOM.unmountComponentAtNode(self.getOrCreateContainer());
if (props && props.onFinished) {
@ -58,17 +58,17 @@ module.exports = {
}
};
var position = {
const position = {
top: props.top,
};
var chevronOffset = {};
const chevronOffset = {};
if (props.chevronOffset) {
chevronOffset.top = props.chevronOffset;
}
// To override the default chevron colour, if it's been set
var chevronCSS = "";
let chevronCSS = "";
if (props.menuColour) {
chevronCSS = `
.mx_ContextualMenu_chevron_left:after {
@ -81,7 +81,7 @@ module.exports = {
`;
}
var chevron = null;
let chevron = null;
if (props.left) {
chevron = <div style={chevronOffset} className="mx_ContextualMenu_chevron_left"></div>;
position.left = props.left;
@ -90,15 +90,15 @@ module.exports = {
position.right = props.right;
}
var className = 'mx_ContextualMenu_wrapper';
const className = 'mx_ContextualMenu_wrapper';
var menuClasses = classNames({
const menuClasses = classNames({
'mx_ContextualMenu': true,
'mx_ContextualMenu_left': props.left,
'mx_ContextualMenu_right': !props.left,
});
var menuStyle = {};
const menuStyle = {};
if (props.menuWidth) {
menuStyle.width = props.menuWidth;
}
@ -113,14 +113,14 @@ module.exports = {
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the menu from a button click!
var menu = (
const menu = (
<div className={className} style={position}>
<div className={menuClasses} style={menuStyle}>
{chevron}
<Element {...props} onFinished={closeMenu}/>
{ chevron }
<Element {...props} onFinished={closeMenu} />
</div>
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
<style>{chevronCSS}</style>
<style>{ chevronCSS }</style>
</div>
);

View file

@ -61,7 +61,7 @@ module.exports = React.createClass({
},
onCreateRoom: function() {
var options = {};
const options = {};
if (this.state.room_name) {
options.name = this.state.room_name;
@ -79,14 +79,14 @@ module.exports = React.createClass({
{
type: "m.room.join_rules",
content: {
"join_rule": this.state.is_private ? "invite" : "public"
}
"join_rule": this.state.is_private ? "invite" : "public",
},
},
{
type: "m.room.history_visibility",
content: {
"history_visibility": this.state.share_history ? "shared" : "invited"
}
"history_visibility": this.state.share_history ? "shared" : "invited",
},
},
];
}
@ -94,19 +94,19 @@ module.exports = React.createClass({
options.invite = this.state.invited_users;
var alias = this.getAliasLocalpart();
const alias = this.getAliasLocalpart();
if (alias) {
options.room_alias_name = alias;
}
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
if (!cli) {
// TODO: Error.
console.error("Cannot create room: No matrix client.");
return;
}
var deferred = cli.createRoom(options);
const deferred = cli.createRoom(options);
if (this.state.encrypt) {
// TODO
@ -116,7 +116,7 @@ module.exports = React.createClass({
phase: this.phases.CREATING,
});
var self = this;
const self = this;
deferred.then(function(resp) {
self.setState({
@ -209,7 +209,7 @@ module.exports = React.createClass({
onAliasChanged: function(alias) {
this.setState({
alias: alias
alias: alias,
});
},
@ -220,64 +220,64 @@ module.exports = React.createClass({
},
render: function() {
var curr_phase = this.state.phase;
const curr_phase = this.state.phase;
if (curr_phase == this.phases.CREATING) {
var Loader = sdk.getComponent("elements.Spinner");
const Loader = sdk.getComponent("elements.Spinner");
return (
<Loader/>
<Loader />
);
} else {
var error_box = "";
let error_box = "";
if (curr_phase == this.phases.ERROR) {
error_box = (
<div className="mx_Error">
{_t('An error occurred: %(error_string)s', {error_string: this.state.error_string})}
{ _t('An error occurred: %(error_string)s', {error_string: this.state.error_string}) }
</div>
);
}
var CreateRoomButton = sdk.getComponent("create_room.CreateRoomButton");
var RoomAlias = sdk.getComponent("create_room.RoomAlias");
var Presets = sdk.getComponent("create_room.Presets");
var UserSelector = sdk.getComponent("elements.UserSelector");
var SimpleRoomHeader = sdk.getComponent("rooms.SimpleRoomHeader");
const CreateRoomButton = sdk.getComponent("create_room.CreateRoomButton");
const RoomAlias = sdk.getComponent("create_room.RoomAlias");
const Presets = sdk.getComponent("create_room.Presets");
const UserSelector = sdk.getComponent("elements.UserSelector");
const SimpleRoomHeader = sdk.getComponent("rooms.SimpleRoomHeader");
var domain = MatrixClientPeg.get().getDomain();
const domain = MatrixClientPeg.get().getDomain();
return (
<div className="mx_CreateRoom">
<SimpleRoomHeader title={_t("Create Room")} collapsedRhs={ this.props.collapsedRhs }/>
<SimpleRoomHeader title={_t("Create Room")} collapsedRhs={this.props.collapsedRhs} />
<div className="mx_CreateRoom_body">
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder={_t('Name')}/> <br />
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder={_t('Topic')}/> <br />
<RoomAlias ref="alias" alias={this.state.alias} homeserver={ domain } onChange={this.onAliasChanged}/> <br />
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder={_t('Name')} /> <br />
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder={_t('Topic')} /> <br />
<RoomAlias ref="alias" alias={this.state.alias} homeserver={domain} onChange={this.onAliasChanged} /> <br />
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged} /> <br />
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset} /> <br />
<div>
<label>
<input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/>
{_t('Make this room private')}
<input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged} />
{ _t('Make this room private') }
</label>
</div>
<div>
<label>
<input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/>
{_t('Share message history with new users')}
<input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged} />
{ _t('Share message history with new users') }
</label>
</div>
<div className="mx_CreateRoom_encrypt">
<label>
<input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/>
{_t('Encrypt room')}
<input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged} />
{ _t('Encrypt room') }
</label>
</div>
<div>
<CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br />
</div>
{error_box}
{ error_box }
</div>
</div>
);
}
}
},
});

View file

@ -24,7 +24,7 @@ import { _t, _tJsx } from '../../languageHandler';
/*
* Component which shows the filtered file using a TimelinePanel
*/
var FilePanel = React.createClass({
const FilePanel = React.createClass({
displayName: 'FilePanel',
propTypes: {
@ -55,33 +55,33 @@ var FilePanel = React.createClass({
},
updateTimelineSet: function(roomId) {
var client = MatrixClientPeg.get();
var room = client.getRoom(roomId);
const client = MatrixClientPeg.get();
const room = client.getRoom(roomId);
this.noRoom = !room;
if (room) {
var filter = new Matrix.Filter(client.credentials.userId);
const filter = new Matrix.Filter(client.credentials.userId);
filter.setDefinition(
{
"room": {
"timeline": {
"contains_url": true
"contains_url": true,
},
}
}
},
},
);
// FIXME: we shouldn't be doing this every time we change room - see comment above.
client.getOrCreateFilter("FILTER_FILES_" + client.credentials.userId, filter).then(
(filterId)=>{
filter.filterId = filterId;
var timelineSet = room.getOrCreateFilteredTimelineSet(filter);
const timelineSet = room.getOrCreateFilteredTimelineSet(filter);
this.setState({ timelineSet: timelineSet });
},
(error)=>{
console.error("Failed to get or create file panel filter", error);
}
},
);
} else {
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
@ -92,18 +92,18 @@ var FilePanel = React.createClass({
if (MatrixClientPeg.get().isGuest()) {
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
<div className="mx_RoomView_empty">
{_tJsx("You must <a>register</a> to use this functionality", /<a>(.*?)<\/a>/, (sub) => <a href="#/register" key="sub">{sub}</a>)}
{ _tJsx("You must <a>register</a> to use this functionality", /<a>(.*?)<\/a>/, (sub) => <a href="#/register" key="sub">{ sub }</a>) }
</div>
</div>;
} else if (this.noRoom) {
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
<div className="mx_RoomView_empty">{_t("You must join the room to see its files")}</div>
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
</div>;
}
// wrap a TimelinePanel with the jump-to-event bits turned off.
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
var Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
if (this.state.timelineSet) {
// console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " +
@ -114,17 +114,16 @@ var FilePanel = React.createClass({
manageReadReceipts={false}
manageReadMarkers={false}
timelineSet={this.state.timelineSet}
showUrlPreview = { false }
showUrlPreview = {false}
tileShape="file_grid"
opacity={ this.props.opacity }
opacity={this.props.opacity}
empty={_t('There are no visible files in this room')}
/>
);
}
else {
} else {
return (
<div className="mx_FilePanel">
<Loader/>
<Loader />
</div>
);
}

View file

@ -27,7 +27,8 @@ import AccessibleButton from '../views/elements/AccessibleButton';
import Modal from '../../Modal';
import classnames from 'classnames';
import GroupSummaryStore from '../../stores/GroupSummaryStore';
import GroupStoreCache from '../../stores/GroupStoreCache';
import GroupStore from '../../stores/GroupStore';
const RoomSummaryType = PropTypes.shape({
room_id: PropTypes.string.isRequired,
@ -78,7 +79,7 @@ const CategoryRoomList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return this.context.groupSummaryStore
return this.context.groupStore
.addRoomToGroupSummary(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
@ -157,7 +158,7 @@ const FeaturedRoom = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
this.context.groupSummaryStore.removeRoomFromGroupSummary(
this.context.groupStore.removeRoomFromGroupSummary(
this.props.summaryInfo.room_id,
).catch((err) => {
console.error('Error whilst removing room from group summary', err);
@ -252,7 +253,7 @@ const RoleUserList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return this.context.groupSummaryStore
return this.context.groupStore
.addUserToGroupSummary(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
@ -327,7 +328,7 @@ const FeaturedUser = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
this.context.groupSummaryStore.removeUserFromGroupSummary(
this.context.groupStore.removeUserFromGroupSummary(
this.props.summaryInfo.user_id,
).catch((err) => {
console.error('Error whilst removing user from group summary', err);
@ -373,14 +374,14 @@ const FeaturedUser = React.createClass({
},
});
const GroupSummaryContext = {
groupSummaryStore: React.PropTypes.instanceOf(GroupSummaryStore).isRequired,
const GroupContext = {
groupStore: React.PropTypes.instanceOf(GroupStore).isRequired,
};
CategoryRoomList.contextTypes = GroupSummaryContext;
FeaturedRoom.contextTypes = GroupSummaryContext;
RoleUserList.contextTypes = GroupSummaryContext;
FeaturedUser.contextTypes = GroupSummaryContext;
CategoryRoomList.contextTypes = GroupContext;
FeaturedRoom.contextTypes = GroupContext;
RoleUserList.contextTypes = GroupContext;
FeaturedUser.contextTypes = GroupContext;
export default React.createClass({
displayName: 'GroupView',
@ -390,12 +391,12 @@ export default React.createClass({
},
childContextTypes: {
groupSummaryStore: React.PropTypes.instanceOf(GroupSummaryStore),
groupStore: React.PropTypes.instanceOf(GroupStore),
},
getChildContext: function() {
return {
groupSummaryStore: this._groupSummaryStore,
groupStore: this._groupStore,
};
},
@ -413,14 +414,14 @@ export default React.createClass({
componentWillMount: function() {
this._changeAvatarComponent = null;
this._initGroupSummaryStore(this.props.groupId);
this._initGroupStore(this.props.groupId);
MatrixClientPeg.get().on("Group.myMembership", this._onGroupMyMembership);
},
componentWillUnmount: function() {
MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
this._groupSummaryStore.removeAllListeners();
this._groupStore.removeAllListeners();
},
componentWillReceiveProps: function(newProps) {
@ -429,7 +430,7 @@ export default React.createClass({
summary: null,
error: null,
}, () => {
this._initGroupSummaryStore(newProps.groupId);
this._initGroupStore(newProps.groupId);
});
}
},
@ -440,17 +441,15 @@ export default React.createClass({
this.setState({membershipBusy: false});
},
_initGroupSummaryStore: function(groupId) {
this._groupSummaryStore = new GroupSummaryStore(
MatrixClientPeg.get(), this.props.groupId,
);
this._groupSummaryStore.on('update', () => {
_initGroupStore: function(groupId) {
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
this._groupStore.on('update', () => {
this.setState({
summary: this._groupSummaryStore.getSummary(),
summary: this._groupStore.getSummary(),
error: null,
});
});
this._groupSummaryStore.on('error', (err) => {
this._groupStore.on('error', (err) => {
this.setState({
summary: null,
error: err,
@ -527,7 +526,7 @@ export default React.createClass({
editing: false,
summary: null,
});
this._initGroupSummaryStore(this.props.groupId);
this._initGroupStore(this.props.groupId);
}).catch((e) => {
this.setState({
saving: false,
@ -606,7 +605,7 @@ export default React.createClass({
this.setState({
publicityBusy: true,
});
this._groupSummaryStore.setGroupPublicity(publicity).then(() => {
this._groupStore.setGroupPublicity(publicity).then(() => {
this.setState({
publicityBusy: false,
});

View file

@ -107,7 +107,7 @@ export default React.createClass({
const msg = error.message || error.toString();
this.setState({
errorText: msg
errorText: msg,
});
}).done();
@ -207,7 +207,7 @@ export default React.createClass({
if (this.state.errorText) {
error = (
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
);
}
@ -215,8 +215,8 @@ export default React.createClass({
return (
<div>
<div>
{this._renderCurrentStage()}
{error}
{ this._renderCurrentStage() }
{ error }
</div>
</div>
);

View file

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -164,7 +165,7 @@ export default React.createClass({
case KeyCode.UP:
case KeyCode.DOWN:
if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
let action = ev.keyCode == KeyCode.UP ?
const action = ev.keyCode == KeyCode.UP ?
'view_prev_room' : 'view_next_room';
dis.dispatch({action: action});
handled = true;
@ -206,8 +207,7 @@ export default React.createClass({
_onScrollKeyPressed: function(ev) {
if (this.refs.roomView) {
this.refs.roomView.handleScrollKey(ev);
}
else if (this.refs.roomDirectory) {
} else if (this.refs.roomDirectory) {
this.refs.roomDirectory.handleScrollKey(ev);
}
},
@ -251,11 +251,10 @@ export default React.createClass({
page_element = <UserSettings
onClose={this.props.onUserSettingsClose}
brand={this.props.config.brand}
enableLabs={this.props.config.enableLabs}
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
case PageTypes.MyGroups:
@ -267,7 +266,7 @@ export default React.createClass({
onRoomCreated={this.props.onRoomCreated}
collapsedRhs={this.props.collapseRhs}
/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
case PageTypes.RoomDirectory:
@ -320,7 +319,7 @@ export default React.createClass({
topBar = <MatrixToolbar />;
}
var bodyClasses = 'mx_MatrixChat';
let bodyClasses = 'mx_MatrixChat';
if (topBar) {
bodyClasses += ' mx_MatrixChat_toolbarShowing';
}
@ -330,7 +329,7 @@ export default React.createClass({
return (
<div className='mx_MatrixChat_wrapper'>
{topBar}
{ topBar }
<div className={bodyClasses}>
<LeftPanel
selectedRoom={this.props.currentRoomId}
@ -338,9 +337,9 @@ export default React.createClass({
opacity={this.props.leftOpacity}
/>
<main className='mx_MatrixChat_middlePanel'>
{page_element}
{ page_element }
</main>
{right_panel}
{ right_panel }
</div>
</div>
);

View file

@ -773,15 +773,13 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_set_mxid'});
return;
}
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createTrackedDialog('Create Room', '', TextInputDialog, {
title: _t('Create Room'),
description: _t('Room name (optional)'),
button: _t('Create Room'),
onFinished: (shouldCreate, name) => {
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, {
onFinished: (shouldCreate, name, noFederate) => {
if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
if (noFederate) createOpts.creation_content = {'m.federate': false};
createRoom({createOpts}).done();
}
},

View file

@ -154,15 +154,15 @@ module.exports = React.createClass({
// 0: read marker is within the window
// +1: read marker is below the window
getReadMarkerPosition: function() {
var readMarker = this.refs.readMarkerNode;
var messageWrapper = this.refs.scrollPanel;
const readMarker = this.refs.readMarkerNode;
const messageWrapper = this.refs.scrollPanel;
if (!readMarker || !messageWrapper) {
return null;
}
var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect();
var readMarkerRect = readMarker.getBoundingClientRect();
const wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect();
const readMarkerRect = readMarker.getBoundingClientRect();
// the read-marker pretends to have zero height when it is actually
// two pixels high; +2 here to account for that.
@ -262,7 +262,7 @@ module.exports = React.createClass({
this.eventNodes = {};
var i;
let i;
// first figure out which is the last event in the list which we're
// actually going to show; this allows us to behave slightly
@ -272,9 +272,9 @@ module.exports = React.createClass({
// a local echo, to manage the read-marker.
let lastShownEvent;
var lastShownNonLocalEchoIndex = -1;
let lastShownNonLocalEchoIndex = -1;
for (i = this.props.events.length-1; i >= 0; i--) {
var mxEv = this.props.events[i];
const mxEv = this.props.events[i];
if (!this._shouldShowEvent(mxEv)) {
continue;
}
@ -292,12 +292,12 @@ module.exports = React.createClass({
break;
}
var ret = [];
const ret = [];
var prevEvent = null; // the last event we showed
let prevEvent = null; // the last event we showed
// assume there is no read marker until proven otherwise
var readMarkerVisible = false;
let readMarkerVisible = false;
// if the readmarker has moved, cancel any active ghost.
if (this.currentReadMarkerEventId && this.props.readMarkerEventId &&
@ -309,16 +309,16 @@ module.exports = React.createClass({
const isMembershipChange = (e) => e.getType() === 'm.room.member';
for (i = 0; i < this.props.events.length; i++) {
let mxEv = this.props.events[i];
let eventId = mxEv.getId();
let last = (mxEv === lastShownEvent);
const mxEv = this.props.events[i];
const eventId = mxEv.getId();
const last = (mxEv === lastShownEvent);
const wantTile = this._shouldShowEvent(mxEv);
// Wrap consecutive member events in a ListSummary, ignore if redacted
if (isMembershipChange(mxEv) && wantTile) {
let readMarkerInMels = false;
let ts1 = mxEv.getTs();
const ts1 = mxEv.getTs();
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
// instead will allow new props to be provided. In turn, the shouldComponentUpdate
@ -330,7 +330,7 @@ module.exports = React.createClass({
const key = "membereventlistsummary-" + (prevEvent ? mxEv.getId() : "initial");
if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) {
let dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} showTwelveHour={this.props.isTwelveHour}/></li>;
const dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} showTwelveHour={this.props.isTwelveHour} /></li>;
ret.push(dateSeparator);
}
@ -339,7 +339,7 @@ module.exports = React.createClass({
readMarkerInMels = true;
}
let summarisedEvents = [mxEv];
const summarisedEvents = [mxEv];
for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1];
@ -390,7 +390,7 @@ module.exports = React.createClass({
onToggle={this._onWidgetLoad} // Update scroll state
startExpanded={highlightInMels}
>
{eventTiles}
{ eventTiles }
</MemberEventListSummary>);
if (readMarkerInMels) {
@ -408,7 +408,7 @@ module.exports = React.createClass({
prevEvent = mxEv;
}
var isVisibleReadMarker = false;
let isVisibleReadMarker = false;
if (eventId == this.props.readMarkerEventId) {
var visible = this.props.readMarkerVisible;
@ -448,10 +448,10 @@ module.exports = React.createClass({
_getTilesForEvent: function(prevEvent, mxEv, last) {
const EventTile = sdk.getComponent('rooms.EventTile');
const DateSeparator = sdk.getComponent('messages.DateSeparator');
var ret = [];
const ret = [];
// is this a continuation of the previous message?
var continuation = false;
let continuation = false;
if (prevEvent !== null
&& prevEvent.sender && mxEv.sender
@ -476,8 +476,8 @@ module.exports = React.createClass({
// local echoes have a fake date, which could even be yesterday. Treat them
// as 'today' for the date separators.
var ts1 = mxEv.getTs();
var eventDate = mxEv.getDate();
let ts1 = mxEv.getTs();
let eventDate = mxEv.getDate();
if (mxEv.status) {
eventDate = new Date();
ts1 = eventDate.getTime();
@ -485,19 +485,19 @@ module.exports = React.createClass({
// do we need a date separator since the last event?
if (this._wantsDateSeparator(prevEvent, eventDate)) {
var dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} showTwelveHour={this.props.isTwelveHour}/></li>;
const dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} showTwelveHour={this.props.isTwelveHour} /></li>;
ret.push(dateSeparator);
continuation = false;
}
var eventId = mxEv.getId();
var highlight = (eventId == this.props.highlightedEventId);
const eventId = mxEv.getId();
const highlight = (eventId == this.props.highlightedEventId);
// we can't use local echoes as scroll tokens, because their event IDs change.
// Local echos have a send "status".
var scrollToken = mxEv.status ? undefined : eventId;
const scrollToken = mxEv.status ? undefined : eventId;
var readReceipts;
let readReceipts;
if (this.props.showReadReceipts) {
readReceipts = this._getReadReceiptsForEvent(mxEv);
}
@ -515,8 +515,8 @@ module.exports = React.createClass({
eventSendStatus={mxEv.status}
tileShape={this.props.tileShape}
isTwelveHour={this.props.isTwelveHour}
last={last} isSelectedEvent={highlight}/>
</li>
last={last} isSelectedEvent={highlight} />
</li>,
);
return ret;
@ -551,7 +551,7 @@ module.exports = React.createClass({
if (!room) {
return null;
}
let receipts = [];
const receipts = [];
room.getReceiptsForEvent(event).forEach((r) => {
if (!r.userId || r.type !== "m.read" || r.userId === myUserId) {
return; // ignore non-read receipts and receipts from self.
@ -559,7 +559,7 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isUserIgnored(r.userId)) {
return; // ignore ignored users
}
let member = room.getMember(r.userId);
const member = room.getMember(r.userId);
if (!member) {
return; // ignore unknown user IDs
}
@ -575,7 +575,7 @@ module.exports = React.createClass({
},
_getReadMarkerTile: function(visible) {
var hr;
let hr;
if (visible) {
hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}}
@ -585,7 +585,7 @@ module.exports = React.createClass({
return (
<li key="_readupto" ref="readMarkerNode"
className="mx_RoomView_myReadMarker_container">
{hr}
{ hr }
</li>
);
},
@ -604,7 +604,7 @@ module.exports = React.createClass({
},
_getReadMarkerGhostTile: function() {
var hr = <hr className="mx_RoomView_myReadMarker"
const hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}}
ref={this._startAnimation}
/>;
@ -615,7 +615,7 @@ module.exports = React.createClass({
return (
<li key={"_readuptoghost_"+this.currentGhostEventId}
className="mx_RoomView_myReadMarker_container">
{hr}
{ hr }
</li>
);
},
@ -627,7 +627,7 @@ module.exports = React.createClass({
// once dynamic content in the events load, make the scrollPanel check the
// scroll offsets.
_onWidgetLoad: function() {
var scrollPanel = this.refs.scrollPanel;
const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) {
scrollPanel.forceUpdate();
}
@ -638,9 +638,9 @@ module.exports = React.createClass({
},
render: function() {
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
var Spinner = sdk.getComponent("elements.Spinner");
var topSpinner, bottomSpinner;
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
const Spinner = sdk.getComponent("elements.Spinner");
let topSpinner, bottomSpinner;
if (this.props.backPaginating) {
topSpinner = <li key="_topSpinner"><Spinner /></li>;
}
@ -648,25 +648,25 @@ module.exports = React.createClass({
bottomSpinner = <li key="_bottomSpinner"><Spinner /></li>;
}
var style = this.props.hidden ? { display: 'none' } : {};
const style = this.props.hidden ? { display: 'none' } : {};
style.opacity = this.props.opacity;
var className = this.props.className + " mx_fadable";
let className = this.props.className + " mx_fadable";
if (this.props.alwaysShowTimestamps) {
className += " mx_MessagePanel_alwaysShowTimestamps";
}
return (
<ScrollPanel ref="scrollPanel" className={ className }
onScroll={ this.props.onScroll }
onResize={ this.onResize }
onFillRequest={ this.props.onFillRequest }
onUnfillRequest={ this.props.onUnfillRequest }
style={ style }
stickyBottom={ this.props.stickyBottom }>
{topSpinner}
{this._getEventTiles()}
{bottomSpinner}
<ScrollPanel ref="scrollPanel" className={className}
onScroll={this.props.onScroll}
onResize={this.onResize}
onFillRequest={this.props.onFillRequest}
onUnfillRequest={this.props.onUnfillRequest}
style={style}
stickyBottom={this.props.stickyBottom}>
{ topSpinner }
{ this._getEventTiles() }
{ bottomSpinner }
</ScrollPanel>
);
},

View file

@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ReactDOM = require("react-dom");
const React = require('react');
const ReactDOM = require("react-dom");
import { _t } from '../../languageHandler';
var Matrix = require("matrix-js-sdk");
var sdk = require('../../index');
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
const Matrix = require("matrix-js-sdk");
const sdk = require('../../index');
const MatrixClientPeg = require("../../MatrixClientPeg");
const dis = require("../../dispatcher");
/*
* Component which shows the global notification list using a TimelinePanel
*/
var NotificationPanel = React.createClass({
const NotificationPanel = React.createClass({
displayName: 'NotificationPanel',
propTypes: {
@ -33,10 +33,10 @@ var NotificationPanel = React.createClass({
render: function() {
// wrap a TimelinePanel with the jump-to-event bits turned off.
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
var Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
var timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
const timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
if (timelineSet) {
return (
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
@ -44,18 +44,17 @@ var NotificationPanel = React.createClass({
manageReadReceipts={false}
manageReadMarkers={false}
timelineSet={timelineSet}
showUrlPreview = { false }
opacity={ this.props.opacity }
showUrlPreview = {false}
opacity={this.props.opacity}
tileShape="notif"
empty={ _t('You have no visible notifications') }
empty={_t('You have no visible notifications')}
/>
);
}
else {
} else {
console.error("No notifTimelineSet available!");
return (
<div className="mx_NotificationPanel">
<Loader/>
<Loader />
</div>
);
}

View file

@ -43,6 +43,10 @@ module.exports = React.createClass({
// the end of the live timeline.
atEndOfLiveTimeline: React.PropTypes.bool,
// This is true when the user is alone in the room, but has also sent a message.
// Used to suggest to the user to invite someone
sentMessageAndIsAlone: React.PropTypes.bool,
// true if there is an active call in this room (means we show
// the 'Active Call' text in the status bar if there is nothing
// more interesting)
@ -60,6 +64,14 @@ module.exports = React.createClass({
// 'unsent messages' bar
onCancelAllClick: React.PropTypes.func,
// callback for when the user clicks on the 'invite others' button in the
// 'you are alone' bar
onInviteClick: React.PropTypes.func,
// callback for when the user clicks on the 'stop warning me' button in the
// 'you are alone' bar
onStopWarningClick: React.PropTypes.func,
// callback for when the user clicks on the 'scroll to bottom' button
onScrollToBottomClick: React.PropTypes.func,
@ -103,7 +115,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("sync", this.onSyncStateChange);
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
@ -115,7 +127,7 @@ module.exports = React.createClass({
return;
}
this.setState({
syncState: state
syncState: state,
});
},
@ -126,7 +138,7 @@ module.exports = React.createClass({
},
// Check whether current size is greater than 0, if yes call props.onVisible
_checkSize: function () {
_checkSize: function() {
if (this.props.onVisible && this._getSize()) {
this.props.onVisible();
}
@ -140,7 +152,8 @@ module.exports = React.createClass({
(this.state.usersTyping.length > 0) ||
this.props.numUnreadMessages ||
!this.props.atEndOfLiveTimeline ||
this.props.hasActiveCall
this.props.hasActiveCall ||
this.props.sentMessageAndIsAlone
) {
return STATUS_BAR_EXPANDED;
} else if (this.props.unsentMessageError) {
@ -157,9 +170,9 @@ module.exports = React.createClass({
if (this.props.numUnreadMessages) {
return (
<div className="mx_RoomStatusBar_scrollDownIndicator"
onClick={ this.props.onScrollToBottomClick }>
onClick={this.props.onScrollToBottomClick}>
<img src="img/newmessages.svg" width="24" height="24"
alt=""/>
alt="" />
</div>
);
}
@ -167,18 +180,18 @@ module.exports = React.createClass({
if (!this.props.atEndOfLiveTimeline) {
return (
<div className="mx_RoomStatusBar_scrollDownIndicator"
onClick={ this.props.onScrollToBottomClick }>
onClick={this.props.onScrollToBottomClick}>
<img src="img/scrolldown.svg" width="24" height="24"
alt={ _t("Scroll to bottom of page") }
title={ _t("Scroll to bottom of page") }/>
alt={_t("Scroll to bottom of page")}
title={_t("Scroll to bottom of page")} />
</div>
);
}
if (this.props.hasActiveCall) {
var TintableSvg = sdk.getComponent("elements.TintableSvg");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
<TintableSvg src="img/sound-indicator.svg" width="23" height="20"/>
<TintableSvg src="img/sound-indicator.svg" width="23" height="20" />
);
}
@ -189,7 +202,7 @@ module.exports = React.createClass({
if (wantPlaceholder) {
return (
<div className="mx_RoomStatusBar_typingIndicatorAvatars">
{this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit)}
{ this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit) }
</div>
);
}
@ -221,8 +234,8 @@ module.exports = React.createClass({
if (othersCount > 0) {
avatars.push(
<span className="mx_RoomStatusBar_typingIndicatorRemaining" key="others">
+{othersCount}
</span>
+{ othersCount }
</span>,
);
}
@ -240,12 +253,12 @@ module.exports = React.createClass({
if (this.state.syncState === "ERROR") {
return (
<div className="mx_RoomStatusBar_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
<div className="mx_RoomStatusBar_connectionLostBar_title">
{_t('Connectivity to the server has been lost.')}
{ _t('Connectivity to the server has been lost.') }
</div>
<div className="mx_RoomStatusBar_connectionLostBar_desc">
{_t('Sent messages will be stored until your connection has returned.')}
{ _t('Sent messages will be stored until your connection has returned.') }
</div>
</div>
);
@ -254,18 +267,18 @@ module.exports = React.createClass({
if (this.props.unsentMessageError) {
return (
<div className="mx_RoomStatusBar_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
<div className="mx_RoomStatusBar_connectionLostBar_title">
{ this.props.unsentMessageError }
</div>
<div className="mx_RoomStatusBar_connectionLostBar_desc">
{_tJsx("<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
{ _tJsx("<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
[/<a>(.*?)<\/a>/, /<a>(.*?)<\/a>/],
[
(sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={ this.props.onResendAllClick }>{sub}</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={ this.props.onCancelAllClick }>{sub}</a>,
]
)}
(sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this.props.onResendAllClick}>{ sub }</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this.props.onCancelAllClick}>{ sub }</a>,
],
) }
</div>
</div>
);
@ -275,24 +288,24 @@ module.exports = React.createClass({
// set when you've scrolled up
if (this.props.numUnreadMessages) {
// MUST use var name "count" for pluralization to kick in
var unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages});
const unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages});
return (
<div className="mx_RoomStatusBar_unreadMessagesBar"
onClick={ this.props.onScrollToBottomClick }>
{unreadMsgs}
onClick={this.props.onScrollToBottomClick}>
{ unreadMsgs }
</div>
);
}
const typingString = WhoIsTyping.whoIsTypingString(
this.state.usersTyping,
this.props.whoIsTypingLimit
this.props.whoIsTypingLimit,
);
if (typingString) {
return (
<div className="mx_RoomStatusBar_typingBar">
<EmojiText>{typingString}</EmojiText>
<EmojiText>{ typingString }</EmojiText>
</div>
);
}
@ -300,7 +313,22 @@ module.exports = React.createClass({
if (this.props.hasActiveCall) {
return (
<div className="mx_RoomStatusBar_callBar">
<b>{_t('Active call')}</b>
<b>{ _t('Active call') }</b>
</div>
);
}
// If you're alone in the room, and have sent a message, suggest to invite someone
if (this.props.sentMessageAndIsAlone) {
return (
<div className="mx_RoomStatusBar_isAlone">
{ _tJsx("There's no one else here! Would you like to <a>invite others</a> or <a>stop warning about the empty room</a>?",
[/<a>(.*?)<\/a>/, /<a>(.*?)<\/a>/],
[
(sub) => <a className="mx_RoomStatusBar_resend_link" key="invite" onClick={this.props.onInviteClick}>{ sub }</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="nowarn" onClick={this.props.onStopWarningClick}>{ sub }</a>,
],
) }
</div>
);
}
@ -310,15 +338,15 @@ module.exports = React.createClass({
render: function() {
var content = this._getContent();
var indicator = this._getIndicator(this.state.usersTyping.length > 0);
const content = this._getContent();
const indicator = this._getIndicator(this.state.usersTyping.length > 0);
return (
<div className="mx_RoomStatusBar">
<div className="mx_RoomStatusBar_indicator">
{indicator}
{ indicator }
</div>
{content}
{ content }
</div>
);
},

View file

@ -22,25 +22,25 @@ limitations under the License.
import shouldHideEvent from "../../shouldHideEvent";
var React = require("react");
var ReactDOM = require("react-dom");
const React = require("react");
const ReactDOM = require("react-dom");
import Promise from 'bluebird';
var classNames = require("classnames");
var Matrix = require("matrix-js-sdk");
const classNames = require("classnames");
const Matrix = require("matrix-js-sdk");
import { _t } from '../../languageHandler';
var UserSettingsStore = require('../../UserSettingsStore');
var MatrixClientPeg = require("../../MatrixClientPeg");
var ContentMessages = require("../../ContentMessages");
var Modal = require("../../Modal");
var sdk = require('../../index');
var CallHandler = require('../../CallHandler');
var Resend = require("../../Resend");
var dis = require("../../dispatcher");
var Tinter = require("../../Tinter");
var rate_limited_func = require('../../ratelimitedfunc');
var ObjectUtils = require('../../ObjectUtils');
var Rooms = require('../../Rooms');
const UserSettingsStore = require('../../UserSettingsStore');
const MatrixClientPeg = require("../../MatrixClientPeg");
const ContentMessages = require("../../ContentMessages");
const Modal = require("../../Modal");
const sdk = require('../../index');
const CallHandler = require('../../CallHandler');
const Resend = require("../../Resend");
const dis = require("../../dispatcher");
const Tinter = require("../../Tinter");
const rate_limited_func = require('../../ratelimitedfunc');
const ObjectUtils = require('../../ObjectUtils');
const Rooms = require('../../Rooms');
import KeyCode from '../../KeyCode';
@ -49,7 +49,7 @@ import UserProvider from '../../autocomplete/UserProvider';
import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
let DEBUG = false;
const DEBUG = false;
let debuglog = function() {};
const BROWSER_SUPPORTS_SANDBOX = 'sandbox' in document.createElement('iframe');
@ -117,6 +117,8 @@ module.exports = React.createClass({
guestsCanJoin: false,
canPeek: false,
showApps: false,
isAlone: false,
isPeeking: false,
// error object, as from the matrix client/server API
// If we failed to load information about the room,
@ -266,6 +268,7 @@ module.exports = React.createClass({
console.log("Attempting to peek into room %s", roomId);
this.setState({
peekLoading: true,
isPeeking: true, // this will change to false if peeking fails
});
MatrixClientPeg.get().peekInRoom(roomId).then((room) => {
this.setState({
@ -274,6 +277,11 @@ module.exports = React.createClass({
});
this._onRoomLoaded(room);
}, (err) => {
// Stop peeking if anything went wrong
this.setState({
isPeeking: false,
});
// This won't necessarily be a MatrixError, but we duck-type
// here and say if it's got an 'errcode' key with the right value,
// it means we can't peek.
@ -290,6 +298,7 @@ module.exports = React.createClass({
} else if (room) {
// Stop peeking because we have joined this room previously
MatrixClientPeg.get().stopPeeking();
this.setState({isPeeking: false});
}
},
@ -307,10 +316,10 @@ module.exports = React.createClass({
},
componentDidMount: function() {
var call = this._getCallForRoom();
var callState = call ? call.call_state : "ended";
const call = this._getCallForRoom();
const callState = call ? call.call_state : "ended";
this.setState({
callState: callState
callState: callState,
});
this._updateConfCallNotification();
@ -327,9 +336,8 @@ module.exports = React.createClass({
this.state.room.getJoinedMembers().length == 1 &&
this.state.room.getLiveTimeline() &&
this.state.room.getLiveTimeline().getEvents() &&
this.state.room.getLiveTimeline().getEvents().length <= 6)
{
var inviteBox = document.getElementById("mx_SearchableEntityList_query");
this.state.room.getLiveTimeline().getEvents().length <= 6) {
const inviteBox = document.getElementById("mx_SearchableEntityList_query");
setTimeout(function() {
if (inviteBox) {
inviteBox.focus();
@ -345,7 +353,7 @@ module.exports = React.createClass({
componentDidUpdate: function() {
if (this.refs.roomView) {
var roomView = ReactDOM.findDOMNode(this.refs.roomView);
const roomView = ReactDOM.findDOMNode(this.refs.roomView);
if (!roomView.ondrop) {
roomView.addEventListener('drop', this.onDrop);
roomView.addEventListener('dragover', this.onDragOver);
@ -372,7 +380,7 @@ module.exports = React.createClass({
// is really just for hygiene - we're going to be
// deleted anyway, so it doesn't matter if the event listeners
// don't get cleaned up.
var roomView = ReactDOM.findDOMNode(this.refs.roomView);
const roomView = ReactDOM.findDOMNode(this.refs.roomView);
roomView.removeEventListener('drop', this.onDrop);
roomView.removeEventListener('dragover', this.onDragOver);
roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd);
@ -454,6 +462,8 @@ module.exports = React.createClass({
switch (payload.action) {
case 'message_send_failed':
case 'message_sent':
this._checkIfAlone(this.state.room);
// no break; to intentionally fall through
case 'message_send_cancelled':
this.setState({
unsentMessageError: this._getUnsentMessageError(this.state.room),
@ -478,8 +488,7 @@ module.exports = React.createClass({
if (call) {
callState = call.call_state;
}
else {
} else {
callState = "ended";
}
@ -591,17 +600,17 @@ module.exports = React.createClass({
},
_calculatePeekRules: function(room) {
var guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
this.setState({
guestsCanJoin: true
guestsCanJoin: true,
});
}
var historyVisibility = room.currentState.getStateEvents("m.room.history_visibility", "");
const historyVisibility = room.currentState.getStateEvents("m.room.history_visibility", "");
if (historyVisibility && historyVisibility.getContent().history_visibility === "world_readable") {
this.setState({
canPeek: true
canPeek: true,
});
}
},
@ -610,35 +619,35 @@ module.exports = React.createClass({
// console.log("_updatePreviewUrlVisibility");
// check our per-room overrides
var roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
const roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
if (roomPreviewUrls && roomPreviewUrls.getContent().disable !== undefined) {
this.setState({
showUrlPreview: !roomPreviewUrls.getContent().disable
showUrlPreview: !roomPreviewUrls.getContent().disable,
});
return;
}
// check our global disable override
var userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
const userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
if (userRoomPreviewUrls && userRoomPreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
showUrlPreview: false,
});
return;
}
// check the room state event
var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
const roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
if (roomStatePreviewUrls && roomStatePreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
showUrlPreview: false,
});
return;
}
// otherwise, we assume they're on.
this.setState({
showUrlPreview: true
showUrlPreview: true,
});
},
@ -654,11 +663,11 @@ module.exports = React.createClass({
},
updateTint: function() {
var room = this.state.room;
const room = this.state.room;
if (!room) return;
var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
var color_scheme = {};
const color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
let color_scheme = {};
if (color_scheme_event) {
color_scheme = color_scheme_event.getContent();
// XXX: we should validate the event
@ -676,12 +685,11 @@ module.exports = React.createClass({
onRoomAccountData: function(event, room) {
if (room.roomId == this.state.roomId) {
if (event.getType() === "org.matrix.room.color_scheme") {
var color_scheme = event.getContent();
const color_scheme = event.getContent();
// XXX: we should validate the event
console.log("Tinter.tint from onRoomAccountData");
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
}
else if (event.getType() === "org.matrix.room.preview_urls") {
} else if (event.getType() === "org.matrix.room.preview_urls") {
this._updatePreviewUrlVisibility(room);
}
}
@ -720,7 +728,7 @@ module.exports = React.createClass({
// if we are now a member of the room, where we were not before, that
// means we have finished joining a room we were previously peeking
// into.
var me = MatrixClientPeg.get().credentials.userId;
const me = MatrixClientPeg.get().credentials.userId;
if (this.state.joining && this.state.room.hasMembershipState(me, "join")) {
// Having just joined a room, check to see if it looks like a DM room, and if so,
// mark it as one. This is to work around the fact that some clients don't support
@ -735,9 +743,34 @@ module.exports = React.createClass({
}
}, 500),
_checkIfAlone: function(room) {
let warnedAboutLonelyRoom = false;
if (localStorage) {
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
}
if (warnedAboutLonelyRoom) {
if (this.state.isAlone) this.setState({isAlone: false});
return;
}
const joinedMembers = room.currentState.getMembers().filter(m => m.membership === "join" || m.membership === "invite");
this.setState({isAlone: joinedMembers.length === 1});
},
_getUnsentMessageError: function(room) {
const unsentMessages = this._getUnsentMessages(room);
if (!unsentMessages.length) return "";
if (
unsentMessages.length === 1 &&
unsentMessages[0].error &&
unsentMessages[0].error.data &&
unsentMessages[0].error.data.error &&
unsentMessages[0].error.name !== "UnknownDeviceError"
) {
return unsentMessages[0].error.data.error;
}
for (const event of unsentMessages) {
if (!event.error || event.error.name !== "UnknownDeviceError") {
return _t("Some of your messages have not been sent.");
@ -754,18 +787,18 @@ module.exports = React.createClass({
},
_updateConfCallNotification: function() {
var room = this.state.room;
const room = this.state.room;
if (!room || !this.props.ConferenceHandler) {
return;
}
var confMember = room.getMember(
this.props.ConferenceHandler.getConferenceUserIdForRoom(room.roomId)
const confMember = room.getMember(
this.props.ConferenceHandler.getConferenceUserIdForRoom(room.roomId),
);
if (!confMember) {
return;
}
var confCall = this.props.ConferenceHandler.getConferenceCallForRoom(confMember.roomId);
const confCall = this.props.ConferenceHandler.getConferenceCallForRoom(confMember.roomId);
// A conf call notification should be displayed if there is an ongoing
// conf call but this cilent isn't a part of it.
@ -773,7 +806,7 @@ module.exports = React.createClass({
displayConfCallNotification: (
(!confCall || confCall.call_state === "ended") &&
confMember.membership === "join"
)
),
});
},
@ -788,7 +821,7 @@ module.exports = React.createClass({
if (this.state.searchResults.next_batch) {
debuglog("requesting more search results");
var searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch(
const searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch(
this.state.searchResults);
return this._handleSearchResult(searchPromise);
} else {
@ -805,6 +838,22 @@ module.exports = React.createClass({
Resend.cancelUnsentEvents(this.state.room);
},
onInviteButtonClick: function() {
// call AddressPickerDialog
dis.dispatch({
action: 'view_invite',
roomId: this.state.room.roomId,
});
this.setState({isAlone: false}); // there's a good chance they'll invite someone
},
onStopAloneWarningClick: function() {
if (localStorage) {
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
}
this.setState({isAlone: false});
},
onJoinButtonClicked: function(ev) {
const cli = MatrixClientPeg.get();
@ -883,8 +932,7 @@ module.exports = React.createClass({
numUnreadMessages: 0,
atEndOfLiveTimeline: true,
});
}
else {
} else {
this.setState({
atEndOfLiveTimeline: false,
});
@ -898,10 +946,10 @@ module.exports = React.createClass({
ev.dataTransfer.dropEffect = 'none';
var items = ev.dataTransfer.items;
const items = ev.dataTransfer.items;
if (items.length == 1) {
if (items[0].kind == 'file') {
this.setState({ draggingFile : true });
this.setState({ draggingFile: true });
ev.dataTransfer.dropEffect = 'copy';
}
}
@ -910,8 +958,8 @@ module.exports = React.createClass({
onDrop: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
var files = ev.dataTransfer.files;
this.setState({ draggingFile: false });
const files = ev.dataTransfer.files;
if (files.length == 1) {
this.uploadFile(files[0]);
}
@ -920,7 +968,7 @@ module.exports = React.createClass({
onDragLeaveOrEnd: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
this.setState({ draggingFile: false });
},
uploadFile: function(file) {
@ -930,7 +978,7 @@ module.exports = React.createClass({
}
ContentMessages.sendContentToRoom(
file, this.state.room.roomId, MatrixClientPeg.get()
file, this.state.room.roomId, MatrixClientPeg.get(),
).done(undefined, (error) => {
if (error.name === "UnknownDeviceError") {
dis.dispatch({
@ -969,19 +1017,19 @@ module.exports = React.createClass({
// todo: should cancel any previous search requests.
this.searchId = new Date().getTime();
var filter;
let filter;
if (scope === "Room") {
filter = {
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
rooms: [
this.state.room.roomId
]
this.state.room.roomId,
],
};
}
debuglog("sending search request");
var searchPromise = MatrixClientPeg.get().searchRoomEvents({
const searchPromise = MatrixClientPeg.get().searchRoomEvents({
filter: filter,
term: term,
});
@ -989,11 +1037,11 @@ module.exports = React.createClass({
},
_handleSearchResult: function(searchPromise) {
var self = this;
const self = this;
// keep a record of the current search id, so that if the search terms
// change before we get a response, we can ignore the results.
var localSearchId = this.searchId;
const localSearchId = this.searchId;
this.setState({
searchInProgress: true,
@ -1012,7 +1060,7 @@ module.exports = React.createClass({
// In either case, we want to highlight the literal search term
// whether it was used by the search engine or not.
var highlights = results.highlights;
let highlights = results.highlights;
if (highlights.indexOf(self.state.searchTerm) < 0) {
highlights = highlights.concat(self.state.searchTerm);
}
@ -1020,14 +1068,15 @@ module.exports = React.createClass({
// For overlapping highlights,
// favour longer (more specific) terms first
highlights = highlights.sort(function(a, b) {
return b.length - a.length; });
return b.length - a.length;
});
self.setState({
searchHighlights: highlights,
searchResults: results,
});
}, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Search failed: " + error);
Modal.createTrackedDialog('Search failed', '', ErrorDialog, {
title: _t("Search failed"),
@ -1035,17 +1084,17 @@ module.exports = React.createClass({
});
}).finally(function() {
self.setState({
searchInProgress: false
searchInProgress: false,
});
});
},
getSearchResultTiles: function() {
var EventTile = sdk.getComponent('rooms.EventTile');
var SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
var Spinner = sdk.getComponent("elements.Spinner");
const EventTile = sdk.getComponent('rooms.EventTile');
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
const Spinner = sdk.getComponent("elements.Spinner");
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
// XXX: todo: merge overlapping results somehow?
// XXX: why doesn't searching on name work?
@ -1055,7 +1104,7 @@ module.exports = React.createClass({
return [];
}
var ret = [];
const ret = [];
if (this.state.searchInProgress) {
ret.push(<li key="search-spinner">
@ -1067,32 +1116,32 @@ module.exports = React.createClass({
if (this.state.searchResults.results.length == 0) {
ret.push(<li key="search-top-marker">
<h2 className="mx_RoomView_topMarker">{ _t("No results") }</h2>
</li>
</li>,
);
} else {
ret.push(<li key="search-top-marker">
<h2 className="mx_RoomView_topMarker">{ _t("No more results") }</h2>
</li>
</li>,
);
}
}
// once dynamic content in the search results load, make the scrollPanel check
// the scroll offsets.
var onWidgetLoad = () => {
var scrollPanel = this.refs.searchResultsPanel;
const onWidgetLoad = () => {
const scrollPanel = this.refs.searchResultsPanel;
if (scrollPanel) {
scrollPanel.checkScroll();
}
};
var lastRoomId;
let lastRoomId;
for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) {
var result = this.state.searchResults.results[i];
for (let i = this.state.searchResults.results.length - 1; i >= 0; i--) {
const result = this.state.searchResults.results[i];
var mxEv = result.context.getEvent();
var roomId = mxEv.getRoomId();
const mxEv = result.context.getEvent();
const roomId = mxEv.getRoomId();
if (!EventTile.haveTileForEvent(mxEv)) {
// XXX: can this ever happen? It will make the result count
@ -1102,13 +1151,13 @@ module.exports = React.createClass({
if (this.state.searchScope === 'All') {
if(roomId != lastRoomId) {
var room = cli.getRoom(roomId);
const room = cli.getRoom(roomId);
// XXX: if we've left the room, we might not know about
// it. We should tell the js sdk to go and find out about
// it. But that's not an issue currently, as synapse only
// returns results for rooms we're joined to.
var roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
const roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
ret.push(<li key={mxEv.getId() + "-room"}>
<h1>{ _t("Room") }: { roomName }</h1>
@ -1117,13 +1166,13 @@ module.exports = React.createClass({
}
}
var resultLink = "#/room/"+roomId+"/"+mxEv.getId();
const resultLink = "#/room/"+roomId+"/"+mxEv.getId();
ret.push(<SearchResultTile key={mxEv.getId()}
searchResult={result}
searchHighlights={this.state.searchHighlights}
resultLink={resultLink}
onWidgetLoad={onWidgetLoad}/>);
onWidgetLoad={onWidgetLoad} />);
}
return ret;
},
@ -1143,38 +1192,37 @@ module.exports = React.createClass({
uploadingRoomSettings: true,
});
var newName = this.refs.header.getEditedName();
const newName = this.refs.header.getEditedName();
if (newName !== undefined) {
this.refs.room_settings.setName(newName);
}
var newTopic = this.refs.header.getEditedTopic();
const newTopic = this.refs.header.getEditedTopic();
if (newTopic !== undefined) {
this.refs.room_settings.setTopic(newTopic);
}
this.refs.room_settings.save().then((results) => {
var fails = results.filter(function(result) { return result.state !== "fulfilled"; });
const fails = results.filter(function(result) { return result.state !== "fulfilled"; });
console.log("Settings saved with %s errors", fails.length);
if (fails.length) {
fails.forEach(function(result) {
console.error(result.reason);
});
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to save room settings', '', ErrorDialog, {
title: _t("Failed to save settings"),
description: fails.map(function(result) { return result.reason; }).join("\n"),
});
// still editing room settings
}
else {
} else {
this.setState({
editingRoomSettings: false
editingRoomSettings: false,
});
}
}).finally(() => {
this.setState({
uploadingRoomSettings: false,
editingRoomSettings: false
editingRoomSettings: false,
});
}).done();
},
@ -1205,8 +1253,8 @@ module.exports = React.createClass({
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
var errCode = err.errcode || _t("unknown error code");
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const errCode = err.errcode || _t("unknown error code");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
title: _t("Error"),
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
@ -1215,20 +1263,20 @@ module.exports = React.createClass({
},
onRejectButtonClicked: function(ev) {
var self = this;
const self = this;
this.setState({
rejecting: true
rejecting: true,
});
MatrixClientPeg.get().leave(this.state.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
self.setState({
rejecting: false
rejecting: false,
});
}, function(error) {
console.error("Failed to reject invite: %s", error);
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
title: _t("Failed to reject invite"),
description: msg,
@ -1236,7 +1284,7 @@ module.exports = React.createClass({
self.setState({
rejecting: false,
rejectError: error
rejectError: error,
});
});
},
@ -1296,7 +1344,7 @@ module.exports = React.createClass({
// restored when we switch back to it.
//
_getScrollState: function() {
var messagePanel = this.refs.messagePanel;
const messagePanel = this.refs.messagePanel;
if (!messagePanel) return null;
// if we're following the live timeline, we want to return null; that
@ -1311,7 +1359,7 @@ module.exports = React.createClass({
return null;
}
var scrollState = messagePanel.getScrollState();
const scrollState = messagePanel.getScrollState();
if (scrollState.stuckAtBottom) {
// we don't really expect to be in this state, but it will
@ -1338,7 +1386,7 @@ module.exports = React.createClass({
// a maxHeight on the underlying remote video tag.
// header + footer + status + give us at least 120px of scrollback at all times.
var auxPanelMaxHeight = window.innerHeight -
let auxPanelMaxHeight = window.innerHeight -
(83 + // height of RoomHeader
36 + // height of the status area
72 + // minimum height of the message compmoser
@ -1357,26 +1405,26 @@ module.exports = React.createClass({
onFullscreenClick: function() {
dis.dispatch({
action: 'video_fullscreen',
fullscreen: true
fullscreen: true,
}, true);
},
onMuteAudioClick: function() {
var call = this._getCallForRoom();
const call = this._getCallForRoom();
if (!call) {
return;
}
var newState = !call.isMicrophoneMuted();
const newState = !call.isMicrophoneMuted();
call.setMicrophoneMuted(newState);
this.forceUpdate(); // TODO: just update the voip buttons
},
onMuteVideoClick: function() {
var call = this._getCallForRoom();
const call = this._getCallForRoom();
if (!call) {
return;
}
var newState = !call.isLocalVideoMuted();
const newState = !call.isLocalVideoMuted();
call.setLocalVideoMuted(newState);
this.forceUpdate(); // TODO: just update the voip buttons
},
@ -1412,7 +1460,7 @@ module.exports = React.createClass({
* We pass it down to the scroll panel.
*/
handleScrollKey: function(ev) {
var panel;
let panel;
if(this.refs.searchResultsPanel) {
panel = this.refs.searchResultsPanel;
} else if(this.refs.messagePanel) {
@ -1483,13 +1531,13 @@ module.exports = React.createClass({
<RoomHeader ref="header"
room={this.state.room}
oobData={this.props.oobData}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
/>
<div className="mx_RoomView_auxPanel">
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
onForgetClick={ this.onForgetClick }
onRejectClick={ this.onRejectThreepidInviteButtonClicked }
canPreview={ false } error={ this.state.roomLoadError }
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectThreepidInviteButtonClicked}
canPreview={false} error={this.state.roomLoadError}
roomAlias={roomAlias}
spinner={this.state.joining}
inviterName={inviterName}
@ -1503,8 +1551,8 @@ module.exports = React.createClass({
}
}
var myUserId = MatrixClientPeg.get().credentials.userId;
var myMember = this.state.room.getMember(myUserId);
const myUserId = MatrixClientPeg.get().credentials.userId;
const myMember = this.state.room.getMember(myUserId);
if (myMember && myMember.membership == 'invite') {
if (this.state.joining || this.state.rejecting) {
return (
@ -1513,7 +1561,7 @@ module.exports = React.createClass({
</div>
);
} else {
var inviteEvent = myMember.events.member;
const inviteEvent = myMember.events.member;
var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
// We deliberately don't try to peek into invites, even if we have permission to peek
@ -1526,14 +1574,14 @@ module.exports = React.createClass({
<RoomHeader
ref="header"
room={this.state.room}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
/>
<div className="mx_RoomView_auxPanel">
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
onForgetClick={ this.onForgetClick }
onRejectClick={ this.onRejectButtonClicked }
inviterName={ inviterName }
canPreview={ false }
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectButtonClicked}
inviterName={inviterName}
canPreview={false}
spinner={this.state.joining}
room={this.state.room}
/>
@ -1547,33 +1595,36 @@ module.exports = React.createClass({
// We have successfully loaded this room, and are not previewing.
// Display the "normal" room view.
var call = this._getCallForRoom();
var inCall = false;
const call = this._getCallForRoom();
let inCall = false;
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
inCall = true;
}
var scrollheader_classes = classNames({
const scrollheader_classes = classNames({
mx_RoomView_scrollheader: true,
});
var statusBar;
let statusBar;
let isStatusAreaExpanded = true;
if (ContentMessages.getCurrentUploads().length > 0) {
var UploadBar = sdk.getComponent('structures.UploadBar');
const UploadBar = sdk.getComponent('structures.UploadBar');
statusBar = <UploadBar room={this.state.room} />;
} else if (!this.state.searchResults) {
var RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
const RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
isStatusAreaExpanded = this.state.statusBarVisible;
statusBar = <RoomStatusBar
room={this.state.room}
numUnreadMessages={this.state.numUnreadMessages}
unsentMessageError={this.state.unsentMessageError}
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
sentMessageAndIsAlone={this.state.isAlone}
hasActiveCall={inCall}
onResendAllClick={this.onResendAllClick}
onCancelAllClick={this.onCancelAllClick}
onInviteClick={this.onInviteButtonClick}
onStopWarningClick={this.onStopAloneWarningClick}
onScrollToBottomClick={this.jumpToLiveTimeline}
onResize={this.onChildResize}
onVisible={this.onStatusBarVisible}
@ -1587,13 +1638,12 @@ module.exports = React.createClass({
if (this.state.editingRoomSettings) {
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />;
} else if (this.state.uploadingRoomSettings) {
aux = <Loader/>;
aux = <Loader />;
} else if (this.state.forwardingEvent !== null) {
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
} else if (this.state.searching) {
hideCancel = true; // has own cancel
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress}
onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />;
} else if (this.state.showingPinned) {
hideCancel = true; // has own cancel
aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />;
@ -1611,7 +1661,7 @@ module.exports = React.createClass({
hideCancel = true;
aux = (
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={ this.onForgetClick }
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectThreepidInviteButtonClicked}
spinner={this.state.joining}
inviterName={inviterName}
@ -1622,7 +1672,7 @@ module.exports = React.createClass({
);
}
var auxPanel = (
const auxPanel = (
<AuxPanel ref="auxPanel" room={this.state.room}
userId={MatrixClientPeg.get().credentials.userId}
conferenceHandler={this.props.ConferenceHandler}
@ -1635,8 +1685,8 @@ module.exports = React.createClass({
</AuxPanel>
);
var messageComposer, searchInfo;
var canSpeak = (
let messageComposer, searchInfo;
const canSpeak = (
// joined and not showing search results
myMember && (myMember.membership == 'join') && !this.state.searchResults
);
@ -1647,8 +1697,8 @@ module.exports = React.createClass({
onResize={this.onChildResize}
uploadFile={this.uploadFile}
callState={this.state.callState}
opacity={ this.props.opacity }
showApps={ this.state.showApps }
opacity={this.props.opacity}
showApps={this.state.showApps}
/>;
}
@ -1656,19 +1706,19 @@ module.exports = React.createClass({
// in this.state if this is what RoomHeader desires?
if (this.state.searchResults) {
searchInfo = {
searchTerm : this.state.searchTerm,
searchScope : this.state.searchScope,
searchCount : this.state.searchResults.count,
searchTerm: this.state.searchTerm,
searchScope: this.state.searchScope,
searchCount: this.state.searchResults.count,
};
}
if (inCall) {
var zoomButton, voiceMuteButton, videoMuteButton;
let zoomButton, voiceMuteButton, videoMuteButton;
if (call.type === "video") {
zoomButton = (
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title={ _t("Fill screen") }>
<TintableSvg src="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }}/>
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title={_t("Fill screen")}>
<TintableSvg src="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }} />
</div>
);
@ -1676,14 +1726,14 @@ module.exports = React.createClass({
<div className="mx_RoomView_voipButton" onClick={this.onMuteVideoClick}>
<TintableSvg src={call.isLocalVideoMuted() ? "img/video-unmute.svg" : "img/video-mute.svg"}
alt={call.isLocalVideoMuted() ? _t("Click to unmute video") : _t("Click to mute video")}
width="31" height="27"/>
width="31" height="27" />
</div>;
}
voiceMuteButton =
<div className="mx_RoomView_voipButton" onClick={this.onMuteAudioClick}>
<TintableSvg src={call.isMicrophoneMuted() ? "img/voice-unmute.svg" : "img/voice-mute.svg"}
alt={call.isMicrophoneMuted() ? _t("Click to unmute audio") : _t("Click to mute audio")}
width="21" height="26"/>
width="21" height="26" />
</div>;
// wrap the existing status bar into a 'callStatusBar' which adds more knobs.
@ -1693,25 +1743,25 @@ module.exports = React.createClass({
{ videoMuteButton }
{ zoomButton }
{ statusBar }
<TintableSvg className="mx_RoomView_voipChevron" src="img/voip-chevron.svg" width="22" height="17"/>
<TintableSvg className="mx_RoomView_voipChevron" src="img/voip-chevron.svg" width="22" height="17" />
</div>;
}
// if we have search results, we keep the messagepanel (so that it preserves its
// scroll state), but hide it.
var searchResultsPanel;
var hideMessagePanel = false;
let searchResultsPanel;
let hideMessagePanel = false;
if (this.state.searchResults) {
searchResultsPanel = (
<ScrollPanel ref="searchResultsPanel"
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel"
onFillRequest={ this.onSearchResultsFillRequest }
onResize={ this.onSearchResultsResize }
onFillRequest={this.onSearchResultsFillRequest}
onResize={this.onSearchResultsResize}
style={{ opacity: this.props.opacity }}
>
<li className={scrollheader_classes}></li>
{this.getSearchResultTiles()}
{ this.getSearchResultTiles() }
</ScrollPanel>
);
hideMessagePanel = true;
@ -1726,26 +1776,26 @@ module.exports = React.createClass({
}
// console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
var messagePanel = (
const messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef}
timelineSet={this.state.room.getUnfilteredTimelineSet()}
showReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
manageReadReceipts={true}
manageReadMarkers={true}
manageReadReceipts={!this.state.isPeeking}
manageReadMarkers={!this.state.isPeeking}
hidden={hideMessagePanel}
highlightedEventId={highlightedEventId}
eventId={this.state.initialEventId}
eventPixelOffset={this.state.initialEventPixelOffset}
onScroll={ this.onMessageListScroll }
onReadMarkerUpdated={ this._updateTopUnreadMessagesBar }
showUrlPreview = { this.state.showUrlPreview }
opacity={ this.props.opacity }
onScroll={this.onMessageListScroll}
onReadMarkerUpdated={this._updateTopUnreadMessagesBar}
showUrlPreview = {this.state.showUrlPreview}
opacity={this.props.opacity}
className="mx_RoomView_messagePanel"
/>);
var topUnreadMessagesBar = null;
let topUnreadMessagesBar = null;
if (this.state.showTopUnreadMessagesBar) {
var TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
topUnreadMessagesBar = (
<div className="mx_RoomView_topUnreadMessagesBar mx_fadable" style={{ opacity: this.props.opacity }}>
<TopUnreadMessagesBar
@ -1761,13 +1811,13 @@ module.exports = React.createClass({
}
return (
<div className={ "mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "") } ref="roomView">
<div className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
oobData={this.props.oobData}
editing={this.state.editingRoomSettings}
saving={this.state.uploadingRoomSettings}
inRoom={myMember && myMember.membership === 'join'}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick}
onPinnedClick={this.onPinnedClick}

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require("react");
var ReactDOM = require("react-dom");
var GeminiScrollbar = require('react-gemini-scrollbar');
const React = require("react");
const ReactDOM = require("react-dom");
const GeminiScrollbar = require('react-gemini-scrollbar');
import Promise from 'bluebird';
var KeyCode = require('../../KeyCode');
const KeyCode = require('../../KeyCode');
var DEBUG_SCROLL = false;
const DEBUG_SCROLL = false;
// var DEBUG_SCROLL = true;
// The amount of extra scroll distance to allow prior to unfilling.
@ -178,7 +178,7 @@ module.exports = React.createClass({
},
onScroll: function(ev) {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
debuglog("Scroll event: offset now:", sn.scrollTop,
"_lastSetScroll:", this._lastSetScroll);
@ -238,7 +238,7 @@ module.exports = React.createClass({
// about whether the the content is scrolled down right now, irrespective of
// whether it will stay that way when the children update.
isAtBottom: function() {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
// there seems to be some bug with flexbox/gemini/chrome/richvdh's
// understanding of the box model, wherein the scrollNode ends up 2
@ -281,7 +281,7 @@ module.exports = React.createClass({
// |#########| |
// `---------' -
_getExcessHeight: function(backwards) {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
if (backwards) {
return sn.scrollTop - sn.clientHeight - UNPAGINATION_PADDING;
} else {
@ -295,7 +295,7 @@ module.exports = React.createClass({
return;
}
var sn = this._getScrollNode();
const sn = this._getScrollNode();
// if there is less than a screenful of messages above or below the
// viewport, try to get some more messages.
@ -377,7 +377,7 @@ module.exports = React.createClass({
// check if there is already a pending fill request. If not, set one off.
_maybeFill: function(backwards) {
var dir = backwards ? 'b' : 'f';
const dir = backwards ? 'b' : 'f';
if (this._pendingFillRequests[dir]) {
debuglog("ScrollPanel: Already a "+dir+" fill in progress - not starting another");
return;
@ -470,8 +470,8 @@ module.exports = React.createClass({
* mult: -1 to page up, +1 to page down
*/
scrollRelative: function(mult) {
var scrollNode = this._getScrollNode();
var delta = mult * scrollNode.clientHeight * 0.5;
const scrollNode = this._getScrollNode();
const delta = mult * scrollNode.clientHeight * 0.5;
this._setScrollTop(scrollNode.scrollTop + delta);
this._saveScrollState();
},
@ -535,7 +535,7 @@ module.exports = React.createClass({
this.scrollState = {
stuckAtBottom: false,
trackedScrollToken: scrollToken,
pixelOffset: pixelOffset
pixelOffset: pixelOffset,
};
// ... then make it so.
@ -546,10 +546,10 @@ module.exports = React.createClass({
// given offset in the window. A helper for _restoreSavedScrollState.
_scrollToToken: function(scrollToken, pixelOffset) {
/* find the dom node with the right scrolltoken */
var node;
var messages = this.refs.itemlist.children;
for (var i = messages.length-1; i >= 0; --i) {
var m = messages[i];
let node;
const messages = this.refs.itemlist.children;
for (let i = messages.length-1; i >= 0; --i) {
const m = messages[i];
// 'data-scroll-tokens' is a DOMString of comma-separated scroll tokens
// There might only be one scroll token
if (m.dataset.scrollTokens &&
@ -564,10 +564,10 @@ module.exports = React.createClass({
return;
}
var scrollNode = this._getScrollNode();
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
const scrollNode = this._getScrollNode();
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
const scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
@ -575,7 +575,6 @@ module.exports = React.createClass({
if(scrollDelta != 0) {
this._setScrollTop(scrollNode.scrollTop + scrollDelta);
}
},
_saveScrollState: function() {
@ -585,16 +584,16 @@ module.exports = React.createClass({
return;
}
var itemlist = this.refs.itemlist;
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
var messages = itemlist.children;
const itemlist = this.refs.itemlist;
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
const messages = itemlist.children;
let newScrollState = null;
for (var i = messages.length-1; i >= 0; --i) {
var node = messages[i];
for (let i = messages.length-1; i >= 0; --i) {
const node = messages[i];
if (!node.dataset.scrollTokens) continue;
var boundingRect = node.getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
newScrollState = {
stuckAtBottom: false,
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
@ -619,8 +618,8 @@ module.exports = React.createClass({
},
_restoreSavedScrollState: function() {
var scrollState = this.scrollState;
var scrollNode = this._getScrollNode();
const scrollState = this.scrollState;
const scrollNode = this._getScrollNode();
if (scrollState.stuckAtBottom) {
this._setScrollTop(Number.MAX_VALUE);
@ -631,9 +630,9 @@ module.exports = React.createClass({
},
_setScrollTop: function(scrollTop) {
var scrollNode = this._getScrollNode();
const scrollNode = this._getScrollNode();
var prevScroll = scrollNode.scrollTop;
const prevScroll = scrollNode.scrollTop;
// FF ignores attempts to set scrollTop to very large numbers
scrollNode.scrollTop = Math.min(scrollTop, scrollNode.scrollHeight);
@ -676,7 +675,7 @@ module.exports = React.createClass({
className={this.props.className} style={this.props.style}>
<div className="mx_RoomView_messageListWrapper">
<ol ref="itemlist" className="mx_RoomView_MessageList" aria-live="polite">
{this.props.children}
{ this.props.children }
</ol>
</div>
</GeminiScrollbar>

View file

@ -15,27 +15,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ReactDOM = require("react-dom");
const React = require('react');
const ReactDOM = require("react-dom");
import Promise from 'bluebird';
var Matrix = require("matrix-js-sdk");
var EventTimeline = Matrix.EventTimeline;
const Matrix = require("matrix-js-sdk");
const EventTimeline = Matrix.EventTimeline;
var sdk = require('../../index');
const sdk = require('../../index');
import { _t } from '../../languageHandler';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
var ObjectUtils = require('../../ObjectUtils');
var Modal = require("../../Modal");
var UserActivity = require("../../UserActivity");
var KeyCode = require('../../KeyCode');
const MatrixClientPeg = require("../../MatrixClientPeg");
const dis = require("../../dispatcher");
const ObjectUtils = require('../../ObjectUtils');
const Modal = require("../../Modal");
const UserActivity = require("../../UserActivity");
const KeyCode = require('../../KeyCode');
import UserSettingsStore from '../../UserSettingsStore';
var PAGINATE_SIZE = 20;
var INITIAL_SIZE = 20;
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
var DEBUG = false;
const DEBUG = false;
if (DEBUG) {
// using bind means that we get to keep useful line numbers in the console
@ -260,7 +260,7 @@ var TimelinePanel = React.createClass({
dis.unregister(this.dispatcherRef);
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("Room.timeline", this.onRoomTimeline);
client.removeListener("Room.timelineReset", this.onRoomTimelineReset);
@ -275,20 +275,20 @@ var TimelinePanel = React.createClass({
onMessageListUnfillRequest: function(backwards, scrollToken) {
// If backwards, unpaginate from the back (i.e. the start of the timeline)
let dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
debuglog("TimelinePanel: unpaginating events in direction", dir);
// All tiles are inserted by MessagePanel to have a scrollToken === eventId, and
// this particular event should be the first or last to be unpaginated.
let eventId = scrollToken;
const eventId = scrollToken;
let marker = this.state.events.findIndex(
const marker = this.state.events.findIndex(
(ev) => {
return ev.getId() === eventId;
}
},
);
let count = backwards ? marker + 1 : this.state.events.length - marker;
const count = backwards ? marker + 1 : this.state.events.length - marker;
if (count > 0) {
debuglog("TimelinePanel: Unpaginating", count, "in direction", dir);
@ -305,9 +305,9 @@ var TimelinePanel = React.createClass({
// set off a pagination request.
onMessageListFillRequest: function(backwards) {
var dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
var canPaginateKey = backwards ? 'canBackPaginate' : 'canForwardPaginate';
var paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating';
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const canPaginateKey = backwards ? 'canBackPaginate' : 'canForwardPaginate';
const paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating';
if (!this.state[canPaginateKey]) {
debuglog("TimelinePanel: have given up", dir, "paginating this timeline");
@ -328,7 +328,7 @@ var TimelinePanel = React.createClass({
debuglog("TimelinePanel: paginate complete backwards:"+backwards+"; success:"+r);
var newState = {
const newState = {
[paginatingKey]: false,
[canPaginateKey]: r,
events: this._getEvents(),
@ -336,8 +336,8 @@ var TimelinePanel = React.createClass({
// moving the window in this direction may mean that we can now
// paginate in the other where we previously could not.
var otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
var canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate';
const otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
const canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate';
if (!this.state[canPaginateOtherWayKey] &&
this._timelineWindow.canPaginate(otherDirection)) {
debuglog('TimelinePanel: can now', otherDirection, 'paginate again');
@ -420,15 +420,15 @@ var TimelinePanel = React.createClass({
this._timelineWindow.paginate(EventTimeline.FORWARDS, 1, false).done(() => {
if (this.unmounted) { return; }
var events = this._timelineWindow.getEvents();
var lastEv = events[events.length-1];
const events = this._timelineWindow.getEvents();
const lastEv = events[events.length-1];
// if we're at the end of the live timeline, append the pending events
if (this.props.timelineSet.room && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
events.push(...this.props.timelineSet.room.getPendingEvents());
}
var updatedState = {events: events};
const updatedState = {events: events};
if (this.props.manageReadMarkers) {
// when a new event arrives when the user is not watching the
@ -439,8 +439,8 @@ var TimelinePanel = React.createClass({
// read-marker when a remote echo of an event we have just sent takes
// more than the timeout on userCurrentlyActive.
//
var myUserId = MatrixClientPeg.get().credentials.userId;
var sender = ev.sender ? ev.sender.userId : null;
const myUserId = MatrixClientPeg.get().credentials.userId;
const sender = ev.sender ? ev.sender.userId : null;
var callback = null;
if (sender != myUserId && !UserActivity.userCurrentlyActive()) {
updatedState.readMarkerVisible = true;
@ -646,7 +646,7 @@ var TimelinePanel = React.createClass({
// and we'll get confused when their ID changes and we can't figure out
// where the RM is pointing to. The read marker will be invisible for
// now anyway, so this doesn't really matter.
var lastDisplayedIndex = this._getLastDisplayedEventIndex({
const lastDisplayedIndex = this._getLastDisplayedEventIndex({
allowPartial: true,
ignoreEchoes: true,
});
@ -655,7 +655,7 @@ var TimelinePanel = React.createClass({
return;
}
var lastDisplayedEvent = this.state.events[lastDisplayedIndex];
const lastDisplayedEvent = this.state.events[lastDisplayedIndex];
this._setReadMarker(lastDisplayedEvent.getId(),
lastDisplayedEvent.getTs());
@ -676,7 +676,7 @@ var TimelinePanel = React.createClass({
// we call _timelineWindow.getEvents() rather than using
// this.state.events, because react batches the update to the latter, so it
// may not have been updated yet.
var events = this._timelineWindow.getEvents();
const events = this._timelineWindow.getEvents();
// first find where the current RM is
for (var i = 0; i < events.length; i++) {
@ -689,7 +689,7 @@ var TimelinePanel = React.createClass({
}
// now think about advancing it
var myUserId = MatrixClientPeg.get().credentials.userId;
const myUserId = MatrixClientPeg.get().credentials.userId;
for (i++; i < events.length; i++) {
var ev = events[i];
if (!ev.sender || ev.sender.userId != myUserId) {
@ -734,7 +734,7 @@ var TimelinePanel = React.createClass({
//
// a quick way to figure out if we've loaded the relevant event is
// simply to check if the messagepanel knows where the read-marker is.
var ret = this.refs.messagePanel.getReadMarkerPosition();
const ret = this.refs.messagePanel.getReadMarkerPosition();
if (ret !== null) {
// The messagepanel knows where the RM is, so we must have loaded
// the relevant event.
@ -755,13 +755,13 @@ var TimelinePanel = React.createClass({
forgetReadMarker: function() {
if (!this.props.manageReadMarkers) return;
var rmId = this._getCurrentReadReceipt();
const rmId = this._getCurrentReadReceipt();
// see if we know the timestamp for the rr event
var tl = this.props.timelineSet.getTimelineForEvent(rmId);
var rmTs;
const tl = this.props.timelineSet.getTimelineForEvent(rmId);
let rmTs;
if (tl) {
var event = tl.getEvents().find((e) => { return e.getId() == rmId; });
const event = tl.getEvents().find((e) => { return e.getId() == rmId; });
if (event) {
rmTs = event.getTs();
}
@ -801,7 +801,7 @@ var TimelinePanel = React.createClass({
if (!this.props.manageReadMarkers) return null;
if (!this.refs.messagePanel) return null;
var ret = this.refs.messagePanel.getReadMarkerPosition();
const ret = this.refs.messagePanel.getReadMarkerPosition();
if (ret !== null) {
return ret;
}
@ -844,8 +844,7 @@ var TimelinePanel = React.createClass({
// jump to the live timeline on ctrl-end, rather than the end of the
// timeline window.
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey &&
ev.keyCode == KeyCode.END)
{
ev.keyCode == KeyCode.END) {
this.jumpToLiveTimeline();
} else {
this.refs.messagePanel.handleScrollKey(ev);
@ -853,12 +852,12 @@ var TimelinePanel = React.createClass({
},
_initTimeline: function(props) {
var initialEvent = props.eventId;
var pixelOffset = props.eventPixelOffset;
const initialEvent = props.eventId;
const pixelOffset = props.eventPixelOffset;
// if a pixelOffset is given, it is relative to the bottom of the
// container. If not, put the event in the middle of the container.
var offsetBase = 1;
let offsetBase = 1;
if (pixelOffset == null) {
offsetBase = 0.5;
}
@ -887,7 +886,7 @@ var TimelinePanel = React.createClass({
MatrixClientPeg.get(), this.props.timelineSet,
{windowLimit: this.props.timelineCap});
var onLoaded = () => {
const onLoaded = () => {
this._reloadEvents();
// If we switched away from the room while there were pending
@ -922,15 +921,15 @@ var TimelinePanel = React.createClass({
});
};
var onError = (error) => {
const onError = (error) => {
this.setState({timelineLoading: false});
console.error(
`Error loading timeline panel at ${eventId}: ${error}`,
);
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var onFinished;
let onFinished;
// if we were given an event ID, then when the user closes the
// dialog, let's jump to the end of the timeline. If we weren't,
@ -945,7 +944,7 @@ var TimelinePanel = React.createClass({
});
};
}
var message = (error.errcode == 'M_FORBIDDEN')
const message = (error.errcode == 'M_FORBIDDEN')
? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.")
: _t("Tried to load a specific point in this room's timeline, but was unable to find it.");
Modal.createTrackedDialog('Failed to load timeline position', '', ErrorDialog, {
@ -955,7 +954,7 @@ var TimelinePanel = React.createClass({
});
};
var prom = this._timelineWindow.load(eventId, INITIAL_SIZE);
let prom = this._timelineWindow.load(eventId, INITIAL_SIZE);
// if we already have the event in question, TimelineWindow.load
// returns a resolved promise.
@ -996,7 +995,7 @@ var TimelinePanel = React.createClass({
// get the list of events from the timeline window and the pending event list
_getEvents: function() {
var events = this._timelineWindow.getEvents();
const events = this._timelineWindow.getEvents();
// if we're at the end of the live timeline, append the pending events
if (!this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
@ -1007,7 +1006,7 @@ var TimelinePanel = React.createClass({
},
_indexForEventId: function(evId) {
for (var i = 0; i < this.state.events.length; ++i) {
for (let i = 0; i < this.state.events.length; ++i) {
if (evId == this.state.events[i].getId()) {
return i;
}
@ -1017,18 +1016,18 @@ var TimelinePanel = React.createClass({
_getLastDisplayedEventIndex: function(opts) {
opts = opts || {};
var ignoreOwn = opts.ignoreOwn || false;
var ignoreEchoes = opts.ignoreEchoes || false;
var allowPartial = opts.allowPartial || false;
const ignoreOwn = opts.ignoreOwn || false;
const ignoreEchoes = opts.ignoreEchoes || false;
const allowPartial = opts.allowPartial || false;
var messagePanel = this.refs.messagePanel;
const messagePanel = this.refs.messagePanel;
if (messagePanel === undefined) return null;
var wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
var myUserId = MatrixClientPeg.get().credentials.userId;
const wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
const myUserId = MatrixClientPeg.get().credentials.userId;
for (var i = this.state.events.length-1; i >= 0; --i) {
var ev = this.state.events[i];
for (let i = this.state.events.length-1; i >= 0; --i) {
const ev = this.state.events[i];
if (ignoreOwn && ev.sender && ev.sender.userId == myUserId) {
continue;
@ -1039,10 +1038,10 @@ var TimelinePanel = React.createClass({
continue;
}
var node = messagePanel.getNodeForEventId(ev.getId());
const node = messagePanel.getNodeForEventId(ev.getId());
if (!node) continue;
var boundingRect = node.getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
if ((allowPartial && boundingRect.top < wrapperRect.bottom) ||
(!allowPartial && boundingRect.bottom < wrapperRect.bottom)) {
return i;
@ -1060,18 +1059,18 @@ var TimelinePanel = React.createClass({
* SDK.
*/
_getCurrentReadReceipt: function(ignoreSynthesized) {
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
// the client can be null on logout
if (client == null) {
return null;
}
var myUserId = client.credentials.userId;
const myUserId = client.credentials.userId;
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
},
_setReadMarker: function(eventId, eventTs, inhibitSetState) {
var roomId = this.props.timelineSet.room.roomId;
const roomId = this.props.timelineSet.room.roomId;
// don't update the state (and cause a re-render) if there is
// no change to the RM.
@ -1096,8 +1095,8 @@ var TimelinePanel = React.createClass({
},
render: function() {
var MessagePanel = sdk.getComponent("structures.MessagePanel");
var Loader = sdk.getComponent("elements.Spinner");
const MessagePanel = sdk.getComponent("structures.MessagePanel");
const Loader = sdk.getComponent("elements.Spinner");
// just show a spinner while the timeline loads.
//
@ -1112,7 +1111,7 @@ var TimelinePanel = React.createClass({
// exist.
if (this.state.timelineLoading) {
return (
<div className={ this.props.className + " mx_RoomView_messageListWrapper" }>
<div className={this.props.className + " mx_RoomView_messageListWrapper"}>
<Loader />
</div>
);
@ -1120,7 +1119,7 @@ var TimelinePanel = React.createClass({
if (this.state.events.length == 0 && !this.state.canBackPaginate && this.props.empty) {
return (
<div className={ this.props.className + " mx_RoomView_messageListWrapper" }>
<div className={this.props.className + " mx_RoomView_messageListWrapper"}>
<div className="mx_RoomView_empty">{ this.props.empty }</div>
</div>
);
@ -1134,7 +1133,7 @@ var TimelinePanel = React.createClass({
// forwards, otherwise if somebody hits the bottom of the loaded
// events when viewing historical messages, we get stuck in a loop
// of paginating our way through the entire history of the room.
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
const stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
// If the state is PREPARED, we're still waiting for the js-sdk to sync with
// the HS and fetch the latest events, so we are effectively forward paginating.
@ -1143,26 +1142,26 @@ var TimelinePanel = React.createClass({
);
return (
<MessagePanel ref="messagePanel"
hidden={ this.props.hidden }
backPaginating={ this.state.backPaginating }
forwardPaginating={ forwardPaginating }
events={ this.state.events }
highlightedEventId={ this.props.highlightedEventId }
readMarkerEventId={ this.state.readMarkerEventId }
readMarkerVisible={ this.state.readMarkerVisible }
suppressFirstDateSeparator={ this.state.canBackPaginate }
showUrlPreview={ this.props.showUrlPreview }
showReadReceipts={ this.props.showReadReceipts }
ourUserId={ MatrixClientPeg.get().credentials.userId }
stickyBottom={ stickyBottom }
onScroll={ this.onMessageListScroll }
onFillRequest={ this.onMessageListFillRequest }
onUnfillRequest={ this.onMessageListUnfillRequest }
opacity={ this.props.opacity }
isTwelveHour={ this.state.isTwelveHour }
alwaysShowTimestamps={ this.state.alwaysShowTimestamps }
className={ this.props.className }
tileShape={ this.props.tileShape }
hidden={this.props.hidden}
backPaginating={this.state.backPaginating}
forwardPaginating={forwardPaginating}
events={this.state.events}
highlightedEventId={this.props.highlightedEventId}
readMarkerEventId={this.state.readMarkerEventId}
readMarkerVisible={this.state.readMarkerVisible}
suppressFirstDateSeparator={this.state.canBackPaginate}
showUrlPreview={this.props.showUrlPreview}
showReadReceipts={this.props.showReadReceipts}
ourUserId={MatrixClientPeg.get().credentials.userId}
stickyBottom={stickyBottom}
onScroll={this.onMessageListScroll}
onFillRequest={this.onMessageListFillRequest}
onUnfillRequest={this.onMessageListUnfillRequest}
opacity={this.props.opacity}
isTwelveHour={this.state.isTwelveHour}
alwaysShowTimestamps={this.state.alwaysShowTimestamps}
className={this.props.className}
tileShape={this.props.tileShape}
/>
);
},

View file

@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ContentMessages = require('../../ContentMessages');
var dis = require('../../dispatcher');
var filesize = require('filesize');
const React = require('react');
const ContentMessages = require('../../ContentMessages');
const dis = require('../../dispatcher');
const filesize = require('filesize');
import { _t } from '../../languageHandler';
module.exports = React.createClass({displayName: 'UploadBar',
propTypes: {
room: React.PropTypes.object
room: React.PropTypes.object,
},
componentDidMount: function() {
@ -46,7 +46,7 @@ module.exports = React.createClass({displayName: 'UploadBar',
},
render: function() {
var uploads = ContentMessages.getCurrentUploads();
const uploads = ContentMessages.getCurrentUploads();
// for testing UI... - also fix up the ContentMessages.getCurrentUploads().length
// check in RoomView
@ -62,8 +62,8 @@ module.exports = React.createClass({displayName: 'UploadBar',
return <div />;
}
var upload;
for (var i = 0; i < uploads.length; ++i) {
let upload;
for (let i = 0; i < uploads.length; ++i) {
if (uploads[i].roomId == this.props.room.roomId) {
upload = uploads[i];
break;
@ -73,32 +73,32 @@ module.exports = React.createClass({displayName: 'UploadBar',
return <div />;
}
var innerProgressStyle = {
width: ((upload.loaded / (upload.total || 1)) * 100) + '%'
const innerProgressStyle = {
width: ((upload.loaded / (upload.total || 1)) * 100) + '%',
};
var uploadedSize = filesize(upload.loaded);
var totalSize = filesize(upload.total);
let uploadedSize = filesize(upload.loaded);
const totalSize = filesize(upload.total);
if (uploadedSize.replace(/^.* /, '') === totalSize.replace(/^.* /, '')) {
uploadedSize = uploadedSize.replace(/ .*/, '');
}
// MUST use var name 'count' for pluralization to kick in
var uploadText = _t("Uploading %(filename)s and %(count)s others", {filename: upload.fileName, count: (uploads.length - 1)});
const uploadText = _t("Uploading %(filename)s and %(count)s others", {filename: upload.fileName, count: (uploads.length - 1)});
return (
<div className="mx_UploadBar">
<div className="mx_UploadBar_uploadProgressOuter">
<div className="mx_UploadBar_uploadProgressInner" style={innerProgressStyle}></div>
</div>
<img className="mx_UploadBar_uploadIcon mx_filterFlipColor" src="img/fileicon.png" width="17" height="22"/>
<img className="mx_UploadBar_uploadIcon mx_filterFlipColor" src="img/fileicon.png" width="17" height="22" />
<img className="mx_UploadBar_uploadCancel mx_filterFlipColor" src="img/cancel.svg" width="18" height="18"
onClick={function() { ContentMessages.cancelUpload(upload.promise); }}
/>
<div className="mx_UploadBar_uploadBytes">
{ uploadedSize } / { totalSize }
</div>
<div className="mx_UploadBar_uploadFilename">{uploadText}</div>
<div className="mx_UploadBar_uploadFilename">{ uploadText }</div>
</div>
);
}
},
});

View file

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -32,7 +33,7 @@ const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import Analytics from '../../Analytics';
import AccessibleButton from '../views/elements/AccessibleButton';
import { _t } from '../../languageHandler';
import { _t, _td } from '../../languageHandler';
import * as languageHandler from '../../languageHandler';
import * as FormattingUtils from '../../utils/FormattingUtils';
@ -63,55 +64,59 @@ const gHVersionLabel = function(repo, token='') {
const SETTINGS_LABELS = [
{
id: 'autoplayGifsAndVideos',
label: 'Autoplay GIFs and videos',
label: _td('Autoplay GIFs and videos'),
},
{
id: 'hideReadReceipts',
label: 'Hide read receipts',
label: _td('Hide read receipts'),
},
{
id: 'dontSendTypingNotifications',
label: "Don't send typing notifications",
label: _td("Don't send typing notifications"),
},
{
id: 'alwaysShowTimestamps',
label: 'Always show message timestamps',
label: _td('Always show message timestamps'),
},
{
id: 'showTwelveHourTimestamps',
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
label: _td('Show timestamps in 12 hour format (e.g. 2:30pm)'),
},
{
id: 'hideJoinLeaves',
label: 'Hide join/leave messages (invites/kicks/bans unaffected)',
label: _td('Hide join/leave messages (invites/kicks/bans unaffected)'),
},
{
id: 'hideAvatarDisplaynameChanges',
label: 'Hide avatar and display name changes',
label: _td('Hide avatar and display name changes'),
},
{
id: 'useCompactLayout',
label: 'Use compact timeline layout',
label: _td('Use compact timeline layout'),
},
{
id: 'hideRedactions',
label: 'Hide removed messages',
label: _td('Hide removed messages'),
},
{
id: 'enableSyntaxHighlightLanguageDetection',
label: 'Enable automatic language detection for syntax highlighting',
label: _td('Enable automatic language detection for syntax highlighting'),
},
{
id: 'MessageComposerInput.autoReplaceEmoji',
label: 'Automatically replace plain text Emoji',
label: _td('Automatically replace plain text Emoji'),
},
{
id: 'MessageComposerInput.dontSuggestEmoji',
label: 'Disable Emoji suggestions while typing',
label: _td('Disable Emoji suggestions while typing'),
},
{
id: 'Pill.shouldHidePillAvatar',
label: 'Hide avatars in user and room mentions',
label: _td('Hide avatars in user and room mentions'),
},
{
id: 'TextualBody.disableBigEmoji',
label: _td('Disable big emoji in chat'),
},
/*
{
@ -124,7 +129,7 @@ const SETTINGS_LABELS = [
const ANALYTICS_SETTINGS_LABELS = [
{
id: 'analyticsOptOut',
label: 'Opt out of analytics',
label: _td('Opt out of analytics'),
fn: function(checked) {
Analytics[checked ? 'disable' : 'enable']();
},
@ -134,7 +139,7 @@ const ANALYTICS_SETTINGS_LABELS = [
const WEBRTC_SETTINGS_LABELS = [
{
id: 'webRtcForceTURN',
label: 'Disable Peer-to-Peer for 1:1 calls',
label: _td('Disable Peer-to-Peer for 1:1 calls'),
},
];
@ -143,7 +148,7 @@ const WEBRTC_SETTINGS_LABELS = [
const CRYPTO_SETTINGS_LABELS = [
{
id: 'blacklistUnverifiedDevices',
label: 'Never send encrypted messages to unverified devices from this device',
label: _td('Never send encrypted messages to unverified devices from this device'),
fn: function(checked) {
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
},
@ -166,12 +171,12 @@ const CRYPTO_SETTINGS_LABELS = [
const THEMES = [
{
id: 'theme',
label: 'Light theme',
label: _td('Light theme'),
value: 'light',
},
{
id: 'theme',
label: 'Dark theme',
label: _td('Dark theme'),
value: 'dark',
},
];
@ -212,9 +217,6 @@ module.exports = React.createClass({
// The brand string given when creating email pushers
brand: React.PropTypes.string,
// True to show the 'labs' section of experimental features
enableLabs: React.PropTypes.bool,
// The base URL to use in the referral link. Defaults to window.location.origin.
referralBaseUrl: React.PropTypes.string,
@ -226,7 +228,6 @@ module.exports = React.createClass({
getDefaultProps: function() {
return {
onClose: function() {},
enableLabs: true,
};
},
@ -426,6 +427,11 @@ module.exports = React.createClass({
});
},
onAvatarRemoveClick: function() {
MatrixClientPeg.get().setAvatarUrl(null);
this.setState({avatarUrl: null}); // the avatar update will complete async for us
},
onLogoutClicked: function(ev) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Logout E2E Export', '', QuestionDialog, {
@ -793,7 +799,7 @@ module.exports = React.createClass({
onChange={onChange}
/>
<label htmlFor={setting.id + "_" + setting.value}>
{ setting.label }
{ _t(setting.label) }
</label>
</div>;
},
@ -923,34 +929,25 @@ module.exports = React.createClass({
},
_renderLabs: function() {
// default to enabled if undefined
if (this.props.enableLabs === false) return null;
UserSettingsStore.doTranslations();
const features = [];
UserSettingsStore.LABS_FEATURES.forEach((feature) => {
// This feature has an override and will be set to the default, so do not
// show it here.
if (feature.override) {
return;
}
UserSettingsStore.getLabsFeatures().forEach((featureId) => {
// TODO: this ought to be a separate component so that we don't need
// to rebind the onChange each time we render
const onChange = (e) => {
UserSettingsStore.setFeatureEnabled(feature.id, e.target.checked);
UserSettingsStore.setFeatureEnabled(featureId, e.target.checked);
this.forceUpdate();
};
features.push(
<div key={feature.id} className="mx_UserSettings_toggle">
<div key={featureId} className="mx_UserSettings_toggle">
<input
type="checkbox"
id={feature.id}
name={feature.id}
defaultChecked={UserSettingsStore.isFeatureEnabled(feature.id)}
id={featureId}
name={featureId}
defaultChecked={UserSettingsStore.isFeatureEnabled(featureId)}
onChange={onChange}
/>
<label htmlFor={feature.id}>{ feature.name }</label>
<label htmlFor={featureId}>{ UserSettingsStore.translatedNameForFeature(featureId) }</label>
</div>);
});
@ -1330,7 +1327,11 @@ module.exports = React.createClass({
</div>
<div className="mx_UserSettings_avatarPicker">
<div onClick={this.onAvatarPickerClick}>
<div className="mx_UserSettings_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
<img src="img/cancel.svg" width="15" height="15"
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
</div>
<div onClick={this.onAvatarPickerClick} className="mx_UserSettings_avatarPicker_imgContainer">
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
showUploadSection={false} className="mx_UserSettings_avatarPicker_img" />
</div>

View file

@ -17,13 +17,13 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
var Modal = require("../../../Modal");
var MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require('../../../index');
const Modal = require("../../../Modal");
const MatrixClientPeg = require('../../../MatrixClientPeg');
var PasswordReset = require("../../../PasswordReset");
const PasswordReset = require("../../../PasswordReset");
module.exports = React.createClass({
displayName: 'ForgotPassword',
@ -35,30 +35,30 @@ module.exports = React.createClass({
customIsUrl: React.PropTypes.string,
onLoginClick: React.PropTypes.func,
onRegisterClick: React.PropTypes.func,
onComplete: React.PropTypes.func.isRequired
onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
progress: null
progress: null,
};
},
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
this.setState({
progress: "sending_email"
progress: "sending_email",
});
this.reset = new PasswordReset(hsUrl, identityUrl);
this.reset.resetPassword(email, password).done(() => {
this.setState({
progress: "sent_email"
progress: "sent_email",
});
}, (err) => {
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
this.setState({
progress: null
progress: null,
});
});
},
@ -81,15 +81,12 @@ module.exports = React.createClass({
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
}
else if (!this.state.password || !this.state.password2) {
} else if (!this.state.password || !this.state.password2) {
this.showErrorDialog(_t('A new password must be entered.'));
}
else if (this.state.password !== this.state.password2) {
} else if (this.state.password !== this.state.password2) {
this.showErrorDialog(_t('New passwords must match each other.'));
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
@ -99,7 +96,7 @@ module.exports = React.createClass({
'end-to-end encryption keys on all devices, ' +
'making encrypted chat history unreadable, ' +
'unless you first export your room keys and re-import ' +
'them afterwards. In future this will be improved.'
'them afterwards. In future this will be improved.',
) }
</div>,
button: _t('Continue'),
@ -107,13 +104,13 @@ module.exports = React.createClass({
<button className="mx_Dialog_primary"
onClick={this._onExportE2eKeysClicked}>
{ _t('Export E2E room keys') }
</button>
</button>,
],
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(
this.state.enteredHomeserverUrl, this.state.enteredIdentityServerUrl,
this.state.email, this.state.password
this.state.email, this.state.password,
);
}
},
@ -133,7 +130,7 @@ module.exports = React.createClass({
onInputChanged: function(stateKey, ev) {
this.setState({
[stateKey]: ev.target.value
[stateKey]: ev.target.value,
});
},
@ -149,7 +146,7 @@ module.exports = React.createClass({
},
showErrorDialog: function(body, title) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title: title,
description: body,
@ -157,37 +154,34 @@ module.exports = React.createClass({
},
render: function() {
var LoginHeader = sdk.getComponent("login.LoginHeader");
var LoginFooter = sdk.getComponent("login.LoginFooter");
var ServerConfig = sdk.getComponent("login.ServerConfig");
var Spinner = sdk.getComponent("elements.Spinner");
const LoginHeader = sdk.getComponent("login.LoginHeader");
const LoginFooter = sdk.getComponent("login.LoginFooter");
const ServerConfig = sdk.getComponent("login.ServerConfig");
const Spinner = sdk.getComponent("elements.Spinner");
var resetPasswordJsx;
let resetPasswordJsx;
if (this.state.progress === "sending_email") {
resetPasswordJsx = <Spinner />;
}
else if (this.state.progress === "sent_email") {
} else if (this.state.progress === "sent_email") {
resetPasswordJsx = (
<div>
{ _t('An email has been sent to') } {this.state.email}. { _t("Once you've followed the link it contains, click below") }.
{ _t('An email has been sent to') } { this.state.email }. { _t("Once you've followed the link it contains, click below") }.
<br />
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
value={ _t('I have verified my email address') } />
value={_t('I have verified my email address')} />
</div>
);
}
else if (this.state.progress === "complete") {
} else if (this.state.progress === "complete") {
resetPasswordJsx = (
<div>
<p>{ _t('Your password has been reset') }.</p>
<p>{ _t('You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device') }.</p>
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
value={ _t('Return to login screen') } />
value={_t('Return to login screen')} />
</div>
);
}
else {
} else {
resetPasswordJsx = (
<div>
<div className="mx_Login_prompt">
@ -199,21 +193,21 @@ module.exports = React.createClass({
name="reset_email" // define a name so browser's password autofill gets less confused
value={this.state.email}
onChange={this.onInputChanged.bind(this, "email")}
placeholder={ _t('Email address') } autoFocus />
placeholder={_t('Email address')} autoFocus />
<br />
<input className="mx_Login_field" ref="pass" type="password"
name="reset_password"
value={this.state.password}
onChange={this.onInputChanged.bind(this, "password")}
placeholder={ _t('New password') } />
placeholder={_t('New password')} />
<br />
<input className="mx_Login_field" ref="pass" type="password"
name="reset_password_confirm"
value={this.state.password2}
onChange={this.onInputChanged.bind(this, "password2")}
placeholder={ _t('Confirm your new password') } />
placeholder={_t('Confirm your new password')} />
<br />
<input className="mx_Login_submit" type="submit" value={ _t('Send Reset Email') } />
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
</form>
<ServerConfig ref="serverConfig"
withToggleButton={true}
@ -222,11 +216,11 @@ module.exports = React.createClass({
customHsUrl={this.props.customHsUrl}
customIsUrl={this.props.customIsUrl}
onServerConfigChange={this.onServerConfigChange}
delayTimeMs={0}/>
delayTimeMs={0} />
<div className="mx_Login_error">
</div>
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
{_t('Return to login screen')}
{ _t('Return to login screen') }
</a>
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
{ _t('Create an account') }
@ -242,9 +236,9 @@ module.exports = React.createClass({
<div className="mx_Login">
<div className="mx_Login_box">
<LoginHeader />
{resetPasswordJsx}
{ resetPasswordJsx }
</div>
</div>
);
}
},
});

View file

@ -134,7 +134,7 @@ module.exports = React.createClass({
},
_onLoginAsGuestClick: function() {
var self = this;
const self = this;
self.setState({
busy: true,
errorText: null,
@ -156,7 +156,7 @@ module.exports = React.createClass({
});
}).finally(function() {
self.setState({
busy: false
busy: false,
});
}).done();
},
@ -183,8 +183,8 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
var self = this;
let newState = {
const self = this;
const newState = {
errorText: null, // reset err messages
};
if (config.hsUrl !== undefined) {
@ -199,13 +199,13 @@ module.exports = React.createClass({
},
_initLoginLogic: function(hsUrl, isUrl) {
var self = this;
const self = this;
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
isUrl = isUrl || this.state.enteredIdentityServerUrl;
var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
const fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
var loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
this._loginLogic = loginLogic;
@ -259,15 +259,15 @@ module.exports = React.createClass({
{ _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
"Either use HTTPS or <a>enable unsafe scripts</a>.",
/<a>(.*?)<\/a>/,
(sub) => { return <a href="https://www.google.com/search?&q=enable%20unsafe%20scripts">{ sub }</a>; }
)}
(sub) => { return <a href="https://www.google.com/search?&q=enable%20unsafe%20scripts">{ sub }</a>; },
) }
</span>;
} else {
errorText = <span>
{ _tJsx("Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.",
/<a>(.*?)<\/a>/,
(sub) => { return <a href={this.state.enteredHomeserverUrl}>{ sub }</a>; }
)}
(sub) => { return <a href={this.state.enteredHomeserverUrl}>{ sub }</a>; },
) }
</span>;
}
}
@ -290,6 +290,7 @@ module.exports = React.createClass({
onPhoneNumberChanged={this.onPhoneNumberChanged}
onForgotPasswordClick={this.props.onForgotPasswordClick}
loginIncorrect={this.state.loginIncorrect}
hsUrl={this.state.enteredHomeserverUrl}
/>
);
case 'm.login.cas':
@ -303,7 +304,7 @@ module.exports = React.createClass({
}
return (
<div>
{ _t('Sorry, this homeserver is using a login which is not recognised ')}({step})
{ _t('Sorry, this homeserver is using a login which is not recognised ') }({ step })
</div>
);
}
@ -333,19 +334,19 @@ module.exports = React.createClass({
const ServerConfig = sdk.getComponent("login.ServerConfig");
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
var loginAsGuestJsx;
let loginAsGuestJsx;
if (this.props.enableGuest) {
loginAsGuestJsx =
<a className="mx_Login_create" onClick={this._onLoginAsGuestClick} href="#">
{ _t('Login as guest')}
{ _t('Login as guest') }
</a>;
}
var returnToAppJsx;
let returnToAppJsx;
if (this.props.onCancelClick) {
returnToAppJsx =
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
{ _t('Return to app')}
{ _t('Return to app') }
</a>;
}
@ -354,7 +355,7 @@ module.exports = React.createClass({
<div className="mx_Login_box">
<LoginHeader />
<div>
<h2>{ _t('Sign in')}
<h2>{ _t('Sign in') }
{ loader }
</h2>
{ this.componentForStep(this.state.currentFlow) }
@ -365,12 +366,12 @@ module.exports = React.createClass({
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
delayTimeMs={1000}/>
delayTimeMs={1000} />
<div className="mx_Login_error">
{ this.state.errorText }
</div>
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
{ _t('Create an account')}
{ _t('Create an account') }
</a>
{ loginAsGuestJsx }
{ returnToAppJsx }
@ -380,5 +381,5 @@ module.exports = React.createClass({
</div>
</div>
);
}
},
});

View file

@ -25,14 +25,14 @@ module.exports = React.createClass({
displayName: 'PostRegistration',
propTypes: {
onComplete: React.PropTypes.func.isRequired
onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
avatarUrl: null,
errorString: null,
busy: false
busy: false,
};
},
@ -40,26 +40,26 @@ module.exports = React.createClass({
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
// the URL to be passed to you (because it's also used for room avatars).
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
this.setState({busy: true});
var self = this;
const self = this;
cli.getProfileInfo(cli.credentials.userId).done(function(result) {
self.setState({
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url),
busy: false
busy: false,
});
}, function(error) {
self.setState({
errorString: _t("Failed to fetch avatar URL"),
busy: false
busy: false,
});
});
},
render: function() {
var ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
var LoginHeader = sdk.getComponent('login.LoginHeader');
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
const LoginHeader = sdk.getComponent('login.LoginHeader');
return (
<div className="mx_Login">
<div className="mx_Login_box">
@ -71,10 +71,10 @@ module.exports = React.createClass({
<ChangeAvatar
initialAvatarUrl={this.state.avatarUrl} />
<button onClick={this.props.onComplete}>{ _t('Continue') }</button>
{this.state.errorString}
{ this.state.errorString }
</div>
</div>
</div>
);
}
},
});

View file

@ -57,7 +57,7 @@ module.exports = React.createClass({
// registration shouldn't know or care how login is done.
onLoginClick: React.PropTypes.func.isRequired,
onCancelClick: React.PropTypes.func
onCancelClick: React.PropTypes.func,
},
getInitialState: function() {
@ -121,7 +121,7 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
let newState = {};
const newState = {};
if (config.hsUrl !== undefined) {
newState.hsUrl = config.hsUrl;
}
@ -195,7 +195,7 @@ module.exports = React.createClass({
this._rtsClient.getTeam(teamToken).then((team) => {
console.log(
`User successfully registered with team ${team.name}`
`User successfully registered with team ${team.name}`,
);
if (!team.rooms) {
return;
@ -223,7 +223,7 @@ module.exports = React.createClass({
deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(),
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
accessToken: response.access_token
accessToken: response.access_token,
}, teamToken);
}).then((cli) => {
return this._setupPushers(cli);
@ -253,7 +253,7 @@ module.exports = React.createClass({
},
onFormValidationFailed: function(errCode) {
var errMsg;
let errMsg;
switch (errCode) {
case "RegistrationForm.ERR_PASSWORD_MISSING":
errMsg = _t('Missing password.');
@ -282,7 +282,7 @@ module.exports = React.createClass({
break;
}
this.setState({
errorText: errMsg
errorText: errMsg,
});
},
@ -316,7 +316,7 @@ module.exports = React.createClass({
emailAddress: this.state.formVals.email,
phoneCountry: this.state.formVals.phoneCountry,
phoneNumber: this.state.formVals.phoneNumber,
}
};
},
render: function() {
@ -346,7 +346,7 @@ module.exports = React.createClass({
} else {
let errorSection;
if (this.state.errorText) {
errorSection = <div className="mx_Login_error">{this.state.errorText}</div>;
errorSection = <div className="mx_Login_error">{ this.state.errorText }</div>;
}
registerBody = (
<div>
@ -362,7 +362,7 @@ module.exports = React.createClass({
onRegisterClick={this.onFormSubmit}
onTeamSelected={this.onTeamSelected}
/>
{errorSection}
{ errorSection }
<ServerConfig ref="serverConfig"
withToggleButton={true}
customHsUrl={this.props.customHsUrl}
@ -380,7 +380,7 @@ module.exports = React.createClass({
if (this.props.onCancelClick) {
returnToAppJsx = (
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
{_t('Return to app')}
{ _t('Return to app') }
</a>
);
}
@ -393,15 +393,15 @@ module.exports = React.createClass({
this.state.teamSelected.domain + "/icon.png" :
null}
/>
<h2>{_t('Create an account')}</h2>
{registerBody}
<h2>{ _t('Create an account') }</h2>
{ registerBody }
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
{_t('I already have an account')}
{ _t('I already have an account') }
</a>
{returnToAppJsx}
{ returnToAppJsx }
<LoginFooter />
</div>
</div>
);
}
},
});

View file

@ -32,7 +32,7 @@ module.exports = React.createClass({
height: React.PropTypes.number,
// XXX resizeMethod not actually used.
resizeMethod: React.PropTypes.string,
defaultToInitialLetter: React.PropTypes.bool // true to add default url
defaultToInitialLetter: React.PropTypes.bool, // true to add default url
},
getDefaultProps: function() {
@ -40,7 +40,7 @@ module.exports = React.createClass({
width: 40,
height: 40,
resizeMethod: 'crop',
defaultToInitialLetter: true
defaultToInitialLetter: true,
};
},
@ -50,15 +50,14 @@ module.exports = React.createClass({
componentWillReceiveProps: function(nextProps) {
// work out if we need to call setState (if the image URLs array has changed)
var newState = this._getState(nextProps);
var newImageUrls = newState.imageUrls;
var oldImageUrls = this.state.imageUrls;
const newState = this._getState(nextProps);
const newImageUrls = newState.imageUrls;
const oldImageUrls = this.state.imageUrls;
if (newImageUrls.length !== oldImageUrls.length) {
this.setState(newState); // detected a new entry
}
else {
} else {
// check each one to see if they are the same
for (var i = 0; i < newImageUrls.length; i++) {
for (let i = 0; i < newImageUrls.length; i++) {
if (oldImageUrls[i] !== newImageUrls[i]) {
this.setState(newState); // detected a diff
break;
@ -71,31 +70,31 @@ module.exports = React.createClass({
// work out the full set of urls to try to load. This is formed like so:
// imageUrls: [ props.url, props.urls, default image ]
var urls = props.urls || [];
const urls = props.urls || [];
if (props.url) {
urls.unshift(props.url); // put in urls[0]
}
var defaultImageUrl = null;
let defaultImageUrl = null;
if (props.defaultToInitialLetter) {
defaultImageUrl = AvatarLogic.defaultAvatarUrlForString(
props.idName || props.name
props.idName || props.name,
);
urls.push(defaultImageUrl); // lowest priority
}
return {
imageUrls: urls,
defaultImageUrl: defaultImageUrl,
urlsIndex: 0
urlsIndex: 0,
};
},
onError: function(ev) {
var nextIndex = this.state.urlsIndex + 1;
const nextIndex = this.state.urlsIndex + 1;
if (nextIndex < this.state.imageUrls.length) {
// try the next one
this.setState({
urlsIndex: nextIndex
urlsIndex: nextIndex,
});
}
},
@ -109,32 +108,32 @@ module.exports = React.createClass({
return undefined;
}
var idx = 0;
var initial = name[0];
let idx = 0;
const initial = name[0];
if ((initial === '@' || initial === '#') && name[1]) {
idx++;
}
// string.codePointAt(0) would do this, but that isn't supported by
// some browsers (notably PhantomJS).
var chars = 1;
var first = name.charCodeAt(idx);
let chars = 1;
const first = name.charCodeAt(idx);
// check if its the start of a surrogate pair
if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
var second = name.charCodeAt(idx+1);
const second = name.charCodeAt(idx+1);
if (second >= 0xDC00 && second <= 0xDFFF) {
chars++;
}
}
var firstChar = name.substring(idx, idx+chars);
const firstChar = name.substring(idx, idx+chars);
return firstChar.toUpperCase();
},
render: function() {
const EmojiText = sdk.getComponent('elements.EmojiText');
var imageUrl = this.state.imageUrls[this.state.urlsIndex];
const imageUrl = this.state.imageUrls[this.state.urlsIndex];
const {
name, idName, title, url, urls, width, height, resizeMethod,
@ -150,7 +149,7 @@ module.exports = React.createClass({
width: width + "px",
lineHeight: height + "px" }}
>
{initialLetter}
{ initialLetter }
</EmojiText>
);
const imgNode = (
@ -163,15 +162,15 @@ module.exports = React.createClass({
<AccessibleButton element='span' className="mx_BaseAvatar"
onClick={onClick} {...otherProps}
>
{textNode}
{imgNode}
{ textNode }
{ imgNode }
</AccessibleButton>
);
} else {
return (
<span className="mx_BaseAvatar" {...otherProps}>
{textNode}
{imgNode}
{ textNode }
{ imgNode }
</span>
);
}
@ -196,5 +195,5 @@ module.exports = React.createClass({
{...otherProps} />
);
}
}
},
});

View file

@ -16,9 +16,9 @@ limitations under the License.
'use strict';
var React = require('react');
var Avatar = require('../../../Avatar');
var sdk = require("../../../index");
const React = require('react');
const Avatar = require('../../../Avatar');
const sdk = require("../../../index");
const dispatcher = require("../../../dispatcher");
module.exports = React.createClass({
@ -63,14 +63,14 @@ module.exports = React.createClass({
imageUrl: Avatar.avatarUrlForMember(props.member,
props.width,
props.height,
props.resizeMethod)
props.resizeMethod),
};
},
render: function() {
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
var {member, onClick, viewUserOnClick, ...otherProps} = this.props;
let {member, onClick, viewUserOnClick, ...otherProps} = this.props;
if (viewUserOnClick) {
onClick = () => {
@ -83,7 +83,7 @@ module.exports = React.createClass({
return (
<BaseAvatar {...otherProps} name={this.state.name} title={this.state.title}
idName={member.userId} url={this.state.imageUrl} onClick={onClick}/>
idName={member.userId} url={this.state.imageUrl} onClick={onClick} />
);
}
},
});

View file

@ -36,7 +36,7 @@ module.exports = React.createClass({
render: function() {
return (
<button className="mx_CreateRoomButton" onClick={this.onClick}>{_t("Create Room")}</button>
<button className="mx_CreateRoomButton" onClick={this.onClick}>{ _t("Create Room") }</button>
);
}
},
});

View file

@ -16,10 +16,10 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
import { _t } from '../../../languageHandler';
var Presets = {
const Presets = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
@ -29,7 +29,7 @@ module.exports = React.createClass({
displayName: 'CreateRoomPresets',
propTypes: {
onChange: React.PropTypes.func,
preset: React.PropTypes.string
preset: React.PropTypes.string,
},
Presets: Presets,
@ -47,10 +47,10 @@ module.exports = React.createClass({
render: function() {
return (
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
<option value={this.Presets.PrivateChat}>{_t("Private Chat")}</option>
<option value={this.Presets.PublicChat}>{_t("Public Chat")}</option>
<option value={this.Presets.Custom}>{_t("Custom")}</option>
<option value={this.Presets.PrivateChat}>{ _t("Private Chat") }</option>
<option value={this.Presets.PublicChat}>{ _t("Public Chat") }</option>
<option value={this.Presets.Custom}>{ _t("Custom") }</option>
</select>
);
}
},
});

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
const React = require('react');
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -35,10 +35,10 @@ module.exports = React.createClass({
},
getAliasLocalpart: function() {
var room_alias = this.props.alias;
let room_alias = this.props.alias;
if (room_alias && this.props.homeserver) {
var suffix = ":" + this.props.homeserver;
const suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
@ -52,22 +52,22 @@ module.exports = React.createClass({
},
onFocus: function(ev) {
var target = ev.target;
var curr_val = ev.target.value;
const target = ev.target;
const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "") {
var self = this;
const self = this;
setTimeout(function() {
target.value = "#:" + self.props.homeserver;
target.setSelectionRange(1, 1);
}, 0);
} else {
var suffix = ":" + this.props.homeserver;
const suffix = ":" + this.props.homeserver;
setTimeout(function() {
target.setSelectionRange(
curr_val.startsWith("#") ? 1 : 0,
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length,
);
}, 0);
}
@ -75,7 +75,7 @@ module.exports = React.createClass({
},
onBlur: function(ev) {
var curr_val = ev.target.value;
const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "#:" + this.props.homeserver) {
@ -84,8 +84,8 @@ module.exports = React.createClass({
}
if (curr_val != "") {
var new_val = ev.target.value;
var suffix = ":" + this.props.homeserver;
let new_val = ev.target.value;
const suffix = ":" + this.props.homeserver;
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
ev.target.value = new_val;
@ -97,7 +97,7 @@ module.exports = React.createClass({
return (
<input type="text" className="mx_RoomAlias" placeholder={_t("Alias (optional)")}
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
value={this.props.alias}/>
value={this.props.alias} />
);
}
},
});

View file

@ -23,6 +23,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import AccessibleButton from '../elements/AccessibleButton';
import Promise from 'bluebird';
import { addressTypes, getAddressType } from '../../../UserAddress.js';
import GroupStoreCache from '../../../stores/GroupStoreCache';
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@ -241,32 +242,25 @@ module.exports = React.createClass({
_doNaiveGroupRoomSearch: function(query) {
const lowerCaseQuery = query.toLowerCase();
MatrixClientPeg.get().getGroupRooms(this.props.groupId).then((resp) => {
const results = [];
resp.chunk.forEach((r) => {
const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery);
const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery);
const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery);
if (!(nameMatch || topicMatch || aliasMatch)) {
return;
}
results.push({
room_id: r.room_id,
avatar_url: r.avatar_url,
name: r.name || r.canonical_alias,
});
});
this._processResults(results, query);
}).catch((err) => {
console.error('Error whilst searching group users: ', err);
this.setState({
searchError: err.errcode ? err.message : _t('Something went wrong!'),
});
}).done(() => {
this.setState({
busy: false,
const groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), this.props.groupId);
const results = [];
groupStore.getGroupRooms().forEach((r) => {
const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery);
const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery);
const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery);
if (!(nameMatch || topicMatch || aliasMatch)) {
return;
}
results.push({
room_id: r.room_id,
avatar_url: r.avatar_url,
name: r.name || r.canonical_alias,
});
});
this._processResults(results, query);
this.setState({
busy: false,
});
},
_doRoomSearch: function(query) {

View file

@ -155,7 +155,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
width={48} height={48}
/>
<div className="mx_ChatCreateOrReuseDialog_profile_name">
{this.state.profile.displayName || this.props.userId}
{ this.state.profile.displayName || this.props.userId }
</div>
</div>;
}
@ -177,7 +177,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
<BaseDialog className='mx_ChatCreateOrReuseDialog'
onFinished={ this.props.onFinished.bind(false) }
onFinished={this.props.onFinished.bind(false)}
title={title}
>
{ content }

View file

@ -0,0 +1,81 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import { _t } from '../../../languageHandler';
export default React.createClass({
displayName: 'CreateRoomDialog',
propTypes: {
onFinished: React.PropTypes.func.isRequired,
},
componentDidMount: function() {
const config = SdkConfig.get();
// Dialog shows inverse of m.federate (noFederate) strict false check to skip undefined check (default = true)
this.defaultNoFederate = config.default_federate === false;
},
onOk: function() {
this.props.onFinished(true, this.refs.textinput.value, this.refs.checkbox.checked);
},
onCancel: function() {
this.props.onFinished(false);
},
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
onEnterPressed={this.onOk}
title={_t('Create Room')}
>
<div className="mx_Dialog_content">
<div className="mx_CreateRoomDialog_label">
<label htmlFor="textinput"> { _t('Room name (optional)') } </label>
</div>
<div>
<input id="textinput" ref="textinput" className="mx_CreateRoomDialog_input" autoFocus={true} size="64" />
</div>
<br />
<details className="mx_CreateRoomDialog_details">
<summary className="mx_CreateRoomDialog_details_summary">{ _t('Advanced options') }</summary>
<div>
<input type="checkbox" id="checkbox" ref="checkbox" defaultChecked={this.defaultNoFederate} />
<label htmlFor="checkbox">
{ _t('Block users on other matrix homeservers from joining this room') }
<br />
({ _t('This setting cannot be changed later!') })
</label>
</div>
</details>
</div>
<div className="mx_Dialog_buttons">
<button onClick={this.onCancel}>
{ _t('Cancel') }
</button>
<button className="mx_Dialog_primary" onClick={this.onOk}>
{ _t('Create Room') }
</button>
</div>
</BaseDialog>
);
},
});

View file

@ -83,7 +83,7 @@ export default class DeactivateAccountDialog extends React.Component {
let error = null;
if (this.state.errStr) {
error = <div className="error">
{this.state.errStr}
{ this.state.errStr }
</div>;
passwordBoxClass = 'error';
}
@ -94,30 +94,30 @@ export default class DeactivateAccountDialog extends React.Component {
let cancelButton = null;
if (!this.state.busy) {
cancelButton = <button onClick={this._onCancel} autoFocus={true}>
{_t("Cancel")}
{ _t("Cancel") }
</button>;
}
return (
<div className="mx_DeactivateAccountDialog">
<div className="mx_Dialog_title danger">
{_t("Deactivate Account")}
{ _t("Deactivate Account") }
</div>
<div className="mx_Dialog_content">
<p>{_t("This will make your account permanently unusable. You will not be able to re-register the same user ID.")}</p>
<p>{ _t("This will make your account permanently unusable. You will not be able to re-register the same user ID.") }</p>
<p>{_t("This action is irreversible.")}</p>
<p>{ _t("This action is irreversible.") }</p>
<p>{_t("To continue, please enter your password.")}</p>
<p>{ _t("To continue, please enter your password.") }</p>
<p>{_t("Password")}:</p>
<p>{ _t("Password") }:</p>
<input
type="password"
onChange={this._onPasswordFieldChange}
ref={(e) => {this._passwordField = e;}}
className={passwordBoxClass}
/>
{error}
{ error }
</div>
<div className="mx_Dialog_buttons">
<button
@ -125,10 +125,10 @@ export default class DeactivateAccountDialog extends React.Component {
onClick={this._onOk}
disabled={!okEnabled}
>
{okLabel}
{ okLabel }
</button>
{cancelButton}
{ cancelButton }
</div>
</div>
);

View file

@ -48,7 +48,7 @@ export default React.createClass({
getInitialState: function() {
return {
authError: null,
}
};
},
_onAuthFinished: function(success, result) {
@ -73,12 +73,12 @@ export default React.createClass({
if (this.state.authError) {
content = (
<div>
<div>{this.state.authError.message || this.state.authError.toString()}</div>
<div>{ this.state.authError.message || this.state.authError.toString() }</div>
<br />
<AccessibleButton onClick={this._onDismissClick}
className="mx_UserSettings_button"
>
{_t("Dismiss")}
{ _t("Dismiss") }
</AccessibleButton>
</div>
);
@ -100,7 +100,7 @@ export default React.createClass({
onFinished={this.props.onFinished}
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
>
{content}
{ content }
</BaseDialog>
);
},

View file

@ -18,7 +18,7 @@ import Modal from '../../../Modal';
import React from 'react';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
/**
* Dialog which asks the user whether they want to share their keys with
@ -116,11 +116,11 @@ export default React.createClass({
let text;
if (this.state.wasNewDevice) {
text = "You added a new device '%(displayName)s', which is"
+ " requesting encryption keys.";
text = _td("You added a new device '%(displayName)s', which is"
+ " requesting encryption keys.");
} else {
text = "Your unverified device '%(displayName)s' is requesting"
+ " encryption keys.";
text = _td("Your unverified device '%(displayName)s' is requesting"
+ " encryption keys.");
}
text = _t(text, {displayName: displayName});

View file

@ -68,7 +68,7 @@ export default React.createClass({
<label htmlFor="textinput"> { this.props.description } </label>
</div>
<div>
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" onKeyDown={this.onKeyDown} />
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" />
</div>
</div>
<div className="mx_Dialog_buttons">

View file

@ -28,9 +28,9 @@ function DeviceListEntry(props) {
return (
<li>
<DeviceVerifyButtons device={ device } userId={ userId } />
<DeviceVerifyButtons device={device} userId={userId} />
{ device.deviceId }
<br/>
<br />
{ device.getDisplayName() }
</li>
);
@ -48,13 +48,13 @@ function UserUnknownDeviceList(props) {
const {userId, userDevices} = props;
const deviceListEntries = Object.keys(userDevices).map((deviceId) =>
<DeviceListEntry key={ deviceId } userId={ userId }
device={ userDevices[deviceId] } />,
<DeviceListEntry key={deviceId} userId={userId}
device={userDevices[deviceId]} />,
);
return (
<ul className="mx_UnknownDeviceDialog_deviceList">
{deviceListEntries}
{ deviceListEntries }
</ul>
);
}
@ -71,13 +71,13 @@ function UnknownDeviceList(props) {
const {devices} = props;
const userListEntries = Object.keys(devices).map((userId) =>
<li key={ userId }>
<li key={userId}>
<p>{ userId }:</p>
<UserUnknownDeviceList userId={ userId } userDevices={ devices[userId] } />
<UserUnknownDeviceList userId={userId} userDevices={devices[userId]} />
</li>,
);
return <ul>{userListEntries}</ul>;
return <ul>{ userListEntries }</ul>;
}
UnknownDeviceList.propTypes = {
@ -120,17 +120,17 @@ export default React.createClass({
if (blacklistUnverified) {
warning = (
<h4>
{_t("You are currently blacklisting unverified devices; to send " +
"messages to these devices you must verify them.")}
{ _t("You are currently blacklisting unverified devices; to send " +
"messages to these devices you must verify them.") }
</h4>
);
} else {
warning = (
<div>
<p>
{_t("We recommend you go through the verification process " +
{ _t("We recommend you go through the verification process " +
"for each device to confirm they belong to their legitimate owner, " +
"but you can resend the message without verifying if you prefer.")}
"but you can resend the message without verifying if you prefer.") }
</p>
</div>
);
@ -149,22 +149,22 @@ export default React.createClass({
>
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
<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>
{ warning }
{_t("Unknown devices")}:
{ _t("Unknown devices") }:
<UnknownDeviceList devices={this.props.devices} />
</GeminiScrollbar>
<div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" autoFocus={ true }
<button className="mx_Dialog_primary" autoFocus={true}
onClick={() => {
this.props.onFinished();
Resend.resendUnsentEvents(this.props.room);
}}>
{_t("Send anyway")}
{ _t("Send anyway") }
</button>
<button className="mx_Dialog_primary" autoFocus={ true }
<button className="mx_Dialog_primary" autoFocus={true}
onClick={() => {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148

View file

@ -32,7 +32,7 @@ export default function AccessibleButton(props) {
};
restProps.tabIndex = restProps.tabIndex || "0";
restProps.role = "button";
restProps.className = (restProps.className ? restProps.className + " " : "") +
restProps.className = (restProps.className ? restProps.className + " " : "") +
"mx_AccessibleButton";
return React.createElement(element, restProps, children);
}

View file

@ -79,8 +79,8 @@ export default React.createClass({
onMouseLeave={this._onMouseLeave}
>
<TintableSvg src={this.props.iconPath} width={this.props.size} height={this.props.size} />
{tooltip}
{ tooltip }
</AccessibleButton>
);
}
},
});

View file

@ -46,8 +46,8 @@ export default React.createClass({
componentWillReceiveProps: function(props) {
// Make sure the selected item isn't outside the list bounds
var selected = this.state.selected;
var maxSelected = this._maxSelected(props.addressList);
const selected = this.state.selected;
const maxSelected = this._maxSelected(props.addressList);
if (selected > maxSelected) {
this.setState({ selected: maxSelected });
}
@ -57,7 +57,7 @@ export default React.createClass({
// As the user scrolls with the arrow keys keep the selected item
// at the top of the window.
if (this.scrollElement && this.props.addressList.length > 0 && !this.state.hover) {
var elementHeight = this.addressListElement.getBoundingClientRect().height;
const elementHeight = this.addressListElement.getBoundingClientRect().height;
this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
}
},
@ -75,7 +75,7 @@ export default React.createClass({
if (this.state.selected > 0) {
this.setState({
selected: this.state.selected - 1,
hover : false,
hover: false,
});
}
},
@ -84,7 +84,7 @@ export default React.createClass({
if (this.state.selected < this._maxSelected(this.props.addressList)) {
this.setState({
selected: this.state.selected + 1,
hover : false,
hover: false,
});
}
},
@ -105,7 +105,7 @@ export default React.createClass({
},
onMouseLeave: function() {
this.setState({ hover : false });
this.setState({ hover: false });
},
selectAddress: function(index) {
@ -117,15 +117,15 @@ export default React.createClass({
},
createAddressListTiles: function() {
var self = this;
var AddressTile = sdk.getComponent("elements.AddressTile");
var maxSelected = this._maxSelected(this.props.addressList);
var addressList = [];
const self = this;
const AddressTile = sdk.getComponent("elements.AddressTile");
const maxSelected = this._maxSelected(this.props.addressList);
const addressList = [];
// Only create the address elements if there are address
if (this.props.addressList.length > 0) {
for (var i = 0; i <= maxSelected; i++) {
var classes = classNames({
for (let i = 0; i <= maxSelected; i++) {
const classes = classNames({
"mx_AddressSelector_addressListElement": true,
"mx_AddressSelector_selected": this.state.selected === i,
});
@ -143,7 +143,7 @@ export default React.createClass({
ref={(ref) => { this.addressListElement = ref; }}
>
<AddressTile address={this.props.addressList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />
</div>
</div>,
);
}
}
@ -151,13 +151,13 @@ export default React.createClass({
},
_maxSelected: function(list) {
var listSize = list.length === 0 ? 0 : list.length - 1;
var maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
const listSize = list.length === 0 ? 0 : list.length - 1;
const maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
return maxSelected;
},
render: function() {
var classes = classNames({
const classes = classNames({
"mx_AddressSelector": true,
"mx_AddressSelector_empty": this.props.addressList.length === 0,
});
@ -168,5 +168,5 @@ export default React.createClass({
{ this.createAddressListTiles() }
</div>
);
}
},
});

View file

@ -107,24 +107,24 @@ export default React.createClass({
let nameNode = null;
if (address.displayName) {
nameNode = <div className={nameClasses}>{ address.displayName }</div>
nameNode = <div className={nameClasses}>{ address.displayName }</div>;
}
info = (
<div className="mx_AddressTile_mx">
<div className={emailClasses}>{ address.address }</div>
{nameNode}
{ nameNode }
</div>
);
} else {
error = true;
var unknownClasses = classNames({
const unknownClasses = classNames({
"mx_AddressTile_unknown": true,
"mx_AddressTile_justified": this.props.justified,
});
info = (
<div className={unknownClasses}>{_t("Unknown Address")}</div>
<div className={unknownClasses}>{ _t("Unknown Address") }</div>
);
}
@ -151,5 +151,5 @@ export default React.createClass({
{ dismiss }
</div>
);
}
},
});

View file

@ -23,7 +23,7 @@ import PlatformPeg from '../../../PlatformPeg';
import ScalarAuthClient from '../../../ScalarAuthClient';
import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index';
import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
@ -195,9 +195,9 @@ export default React.createClass({
// These strings are translated at the point that they are inserted in to the DOM, in the render method
_deleteWidgetLabel() {
if (this._canUserModify()) {
return 'Delete widget';
return _td('Delete widget');
}
return 'Revoke widget access';
return _td('Revoke widget access');
},
/* TODO -- Store permission in account data so that it is persisted across multiple devices */

View file

@ -24,7 +24,7 @@ const CreateRoomButton = function(props) {
return (
<ActionButton action="view_create_room"
mouseOverAction={props.callout ? "callout_create_room" : null}
label={ _t("Create new room") }
label={_t("Create new room")}
iconPath="img/icons-create-room.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -30,7 +30,7 @@ export default React.createClass({
getInitialState: function() {
return {
device: this.props.device
device: this.props.device,
};
},
@ -60,37 +60,37 @@ export default React.createClass({
onUnverifyClick: function() {
MatrixClientPeg.get().setDeviceVerified(
this.props.userId, this.state.device.deviceId, false
this.props.userId, this.state.device.deviceId, false,
);
},
onBlacklistClick: function() {
MatrixClientPeg.get().setDeviceBlocked(
this.props.userId, this.state.device.deviceId, true
this.props.userId, this.state.device.deviceId, true,
);
},
onUnblacklistClick: function() {
MatrixClientPeg.get().setDeviceBlocked(
this.props.userId, this.state.device.deviceId, false
this.props.userId, this.state.device.deviceId, false,
);
},
render: function() {
var blacklistButton = null, verifyButton = null;
let blacklistButton = null, verifyButton = null;
if (this.state.device.isBlocked()) {
blacklistButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unblacklist"
onClick={this.onUnblacklistClick}>
{_t("Unblacklist")}
{ _t("Unblacklist") }
</button>
);
} else {
blacklistButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_blacklist"
onClick={this.onBlacklistClick}>
{_t("Blacklist")}
{ _t("Blacklist") }
</button>
);
}
@ -99,14 +99,14 @@ export default React.createClass({
verifyButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unverify"
onClick={this.onUnverifyClick}>
{_t("Unverify")}
{ _t("Unverify") }
</button>
);
} else {
verifyButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_verify"
onClick={this.onVerifyClick}>
{_t("Verify...")}
{ _t("Verify...") }
</button>
);
}

View file

@ -95,7 +95,7 @@ export default class DirectorySearchBox extends React.Component {
onChange={this._onChange} onKeyUp={this._onKeyUp}
placeholder={this.props.placeholder} autoFocus
/>
{join_button}
{ join_button }
<span className="mx_DirectorySearchBox_clear_wrapper">
<span className="mx_DirectorySearchBox_clear" onClick={this._onClearClick} />
</span>

View file

@ -26,6 +26,12 @@ class MenuOption extends React.Component {
this._onClick = this._onClick.bind(this);
}
getDefaultProps() {
return {
disabled: false,
};
}
_onMouseEnter() {
this.props.onMouseEnter(this.props.dropdownKey);
}
@ -46,15 +52,15 @@ class MenuOption extends React.Component {
onClick={this._onClick} onKeyPress={this._onKeyPress}
onMouseEnter={this._onMouseEnter}
>
{this.props.children}
</div>
{ this.props.children }
</div>;
}
};
}
MenuOption.propTypes = {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.node),
React.PropTypes.node
React.PropTypes.node,
]),
highlighted: React.PropTypes.bool,
dropdownKey: React.PropTypes.string,
@ -153,6 +159,8 @@ export default class Dropdown extends React.Component {
}
_onInputClick(ev) {
if (this.props.disabled) return;
if (!this.state.expanded) {
this.setState({
expanded: true,
@ -250,13 +258,13 @@ export default class Dropdown extends React.Component {
onMouseEnter={this._setHighlightedOption}
onClick={this._onMenuOptionClick}
>
{child}
{ child }
</MenuOption>
);
});
if (options.length === 0) {
return [<div key="0" className="mx_Dropdown_option">
{_t("No results")}
{ _t("No results") }
</div>];
}
return options;
@ -279,7 +287,7 @@ export default class Dropdown extends React.Component {
/>;
}
menu = <div className="mx_Dropdown_menu" style={menuStyle}>
{this._getMenuOptions()}
{ this._getMenuOptions() }
</div>;
}
@ -288,12 +296,13 @@ export default class Dropdown extends React.Component {
this.props.getShortOption(this.props.value) :
this.childrenByKey[this.props.value];
currentValue = <div className="mx_Dropdown_option">
{selectedChild}
</div>
{ selectedChild }
</div>;
}
const dropdownClasses = {
mx_Dropdown: true,
mx_Dropdown_disabled: this.props.disabled,
};
if (this.props.className) {
dropdownClasses[this.props.className] = true;
@ -303,9 +312,9 @@ export default class Dropdown extends React.Component {
// to the input, but overflows below it. The root contains both.
return <div className={classnames(dropdownClasses)} ref={this._collectRoot}>
<AccessibleButton className="mx_Dropdown_input" onClick={this._onInputClick}>
{currentValue}
{ currentValue }
<span className="mx_Dropdown_arrow"></span>
{menu}
{ menu }
</AccessibleButton>
</div>;
}
@ -329,4 +338,6 @@ Dropdown.propTypes = {
// in the dropped-down menu.
getShortOption: React.PropTypes.func,
value: React.PropTypes.string,
}
// negative for consistency with HTML
disabled: React.PropTypes.bool,
};

View file

@ -0,0 +1,149 @@
/*
Copyright 2017 New Vector Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import {_t} from '../../../languageHandler.js';
const EditableItem = React.createClass({
displayName: 'EditableItem',
propTypes: {
initialValue: PropTypes.string,
index: PropTypes.number,
placeholder: PropTypes.string,
onChange: PropTypes.func,
onRemove: PropTypes.func,
onAdd: PropTypes.func,
addOnChange: PropTypes.bool,
},
onChange: function(value) {
this.setState({ value });
if (this.props.onChange) this.props.onChange(value, this.props.index);
if (this.props.addOnChange && this.props.onAdd) this.props.onAdd(value);
},
onRemove: function() {
if (this.props.onRemove) this.props.onRemove(this.props.index);
},
onAdd: function() {
if (this.props.onAdd) this.props.onAdd(this.state.value);
},
render: function() {
const EditableText = sdk.getComponent('elements.EditableText');
return <div className="mx_EditableItem">
<EditableText
className="mx_EditableItem_editable"
placeholderClassName="mx_EditableItem_editablePlaceholder"
placeholder={this.props.placeholder}
blurToCancel={false}
editable={true}
initialValue={this.props.initialValue}
onValueChanged={this.onChange} />
{ this.props.onAdd ?
<div className="mx_EditableItem_addButton">
<img className="mx_filterFlipColor"
src="img/plus.svg" width="14" height="14"
alt={_t("Add")} onClick={this.onAdd} />
</div>
:
<div className="mx_EditableItem_removeButton">
<img className="mx_filterFlipColor"
src="img/cancel-small.svg" width="14" height="14"
alt={_t("Delete")} onClick={this.onRemove} />
</div>
}
</div>;
},
});
module.exports = React.createClass({
displayName: 'EditableItemList',
propTypes: {
items: PropTypes.arrayOf(PropTypes.string).isRequired,
onNewItemChanged: PropTypes.func,
onItemAdded: PropTypes.func,
onItemEdited: PropTypes.func,
onItemRemoved: PropTypes. func,
},
getDefaultProps: function() {
return {
onItemAdded: () => {},
onItemEdited: () => {},
onItemRemoved: () => {},
onNewItemChanged: () => {},
};
},
onItemAdded: function(value) {
this.props.onItemAdded(value);
},
onItemEdited: function(value, index) {
if (value.length === 0) {
this.onItemRemoved(index);
} else {
this.props.onItemEdited(value, index);
}
},
onItemRemoved: function(index) {
this.props.onItemRemoved(index);
},
onNewItemChanged: function(value) {
this.props.onNewItemChanged(value);
},
render: function() {
const editableItems = this.props.items.map((item, index) => {
return <EditableItem
key={index}
index={index}
initialValue={item}
onChange={this.onItemEdited}
onRemove={this.onItemRemoved}
placeholder={this.props.placeholder}
/>;
});
const label = this.props.items.length > 0 ?
this.props.itemsLabel : this.props.noItemsLabel;
return (<div className="mx_EditableItemList">
<div className="mx_EditableItemList_label">
{ label }
</div>
{ editableItems }
<EditableItem
key={-1}
initialValue={this.props.newItem}
onAdd={this.onItemAdded}
onChange={this.onNewItemChanged}
addOnChange={true}
placeholder={this.props.placeholder}
/>
</div>);
},
});

View file

@ -16,7 +16,7 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
const KEY_TAB = 9;
const KEY_SHIFT = 16;
@ -65,7 +65,9 @@ module.exports = React.createClass({
},
componentWillReceiveProps: function(nextProps) {
if (nextProps.initialValue !== this.props.initialValue) {
if (nextProps.initialValue !== this.props.initialValue ||
nextProps.initialValue !== this.value
) {
this.value = nextProps.initialValue;
if (this.refs.editable_div) {
this.showPlaceholder(!this.value);
@ -93,8 +95,7 @@ module.exports = React.createClass({
this.refs.editable_div.setAttribute("class", this.props.className + " " + this.props.placeholderClassName);
this.placeholder = true;
this.value = '';
}
else {
} else {
this.refs.editable_div.textContent = this.value;
this.refs.editable_div.setAttribute("class", this.props.className);
this.placeholder = false;
@ -150,8 +151,7 @@ module.exports = React.createClass({
if (!ev.target.textContent) {
this.showPlaceholder(true);
}
else if (!this.placeholder) {
} else if (!this.placeholder) {
this.value = ev.target.textContent;
}
@ -175,21 +175,21 @@ module.exports = React.createClass({
onFocus: function(ev) {
//ev.target.setSelectionRange(0, ev.target.textContent.length);
var node = ev.target.childNodes[0];
const node = ev.target.childNodes[0];
if (node) {
var range = document.createRange();
const range = document.createRange();
range.setStart(node, 0);
range.setEnd(node, node.length);
var sel = window.getSelection();
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
},
onFinish: function(ev, shouldSubmit) {
var self = this;
var submit = (ev.key === "Enter") || shouldSubmit;
const self = this;
const submit = (ev.key === "Enter") || shouldSubmit;
this.setState({
phase: this.Phases.Display,
}, function() {
@ -200,19 +200,16 @@ module.exports = React.createClass({
},
onBlur: function(ev) {
var sel = window.getSelection();
const sel = window.getSelection();
sel.removeAllRanges();
if (this.props.blurToCancel)
{this.cancelEdit();}
else
{this.onFinish(ev, this.props.blurToSubmit);}
if (this.props.blurToCancel) {this.cancelEdit();} else {this.onFinish(ev, this.props.blurToSubmit);}
this.showPlaceholder(!this.value);
},
render: function() {
var editable_el;
let editable_el;
if (!this.props.editable || (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value)) {
// show the label
@ -224,5 +221,5 @@ module.exports = React.createClass({
}
return editable_el;
}
},
});

View file

@ -64,7 +64,7 @@ export default class EditableTextContainer extends React.Component {
errorString: error.toString(),
busy: false,
});
}
},
);
}
@ -96,22 +96,22 @@ export default class EditableTextContainer extends React.Component {
errorString: error.toString(),
busy: false,
});
}
},
);
}
render() {
if (this.state.busy) {
var Loader = sdk.getComponent("elements.Spinner");
const Loader = sdk.getComponent("elements.Spinner");
return (
<Loader />
);
} else if (this.state.errorString) {
return (
<div className="error">{this.state.errorString}</div>
<div className="error">{ this.state.errorString }</div>
);
} else {
var EditableText = sdk.getComponent('elements.EditableText');
const EditableText = sdk.getComponent('elements.EditableText');
return (
<EditableText initialValue={this.state.value}
placeholder={this.props.placeholder}

View file

@ -183,10 +183,12 @@ export default class Flair extends React.Component {
this.state = {
profiles: [],
};
this.onRoomStateEvents = this.onRoomStateEvents.bind(this);
}
componentWillUnmount() {
this._unmounted = true;
this.context.matrixClient.removeListener('RoomState.events', this.onRoomStateEvents);
}
componentWillMount() {
@ -194,6 +196,13 @@ export default class Flair extends React.Component {
if (UserSettingsStore.isFeatureEnabled('feature_groups') && groupSupport) {
this._generateAvatars();
}
this.context.matrixClient.on('RoomState.events', this.onRoomStateEvents);
}
onRoomStateEvents(event) {
if (event.getType() === 'm.room.related_groups' && groupSupport) {
this._generateAvatars();
}
}
async _getGroupProfiles(groups) {
@ -224,6 +233,21 @@ export default class Flair extends React.Component {
}
console.error('Could not get groups for user', this.props.userId, err);
}
if (this.props.roomId && this.props.showRelated) {
const relatedGroupsEvent = this.context.matrixClient
.getRoom(this.props.roomId)
.currentState
.getStateEvents('m.room.related_groups', '');
const relatedGroups = relatedGroupsEvent ?
relatedGroupsEvent.getContent().groups || [] : [];
if (relatedGroups && relatedGroups.length > 0) {
groups = groups.filter((groupId) => {
return relatedGroups.includes(groupId);
});
} else {
groups = [];
}
}
if (!groups || groups.length === 0) {
return;
}
@ -241,7 +265,7 @@ export default class Flair extends React.Component {
return <FlairAvatar key={index} groupProfile={profile} />;
});
return (
<span className="mx_Flair" style={{"marginLeft": "5px", "verticalAlign": "-3px"}}>
<span className="mx_Flair">
{ avatars }
</span>
);
@ -250,6 +274,12 @@ export default class Flair extends React.Component {
Flair.propTypes = {
userId: PropTypes.string,
// Whether to show only the flair associated with related groups of the given room,
// or all flair associated with a user.
showRelated: PropTypes.bool,
// The room that this flair will be displayed in. Optional. Only applies when showRelated = true.
roomId: PropTypes.string,
};
// TODO: We've decided that all components should follow this pattern, which means removing withMatrixClient and using

View file

@ -23,7 +23,7 @@ const HomeButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
<ActionButton action="view_home_page"
label={ _t("Home") }
label={_t("Home")}
iconPath="img/icons-home.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -35,12 +35,12 @@ export default class LanguageDropdown extends React.Component {
this.state = {
searchQuery: '',
langs: null,
}
};
}
componentWillMount() {
languageHandler.getAllLanguagesFromJson().then((langs) => {
langs.sort(function(a, b){
langs.sort(function(a, b) {
if(a.label < b.label) return -1;
if(a.label > b.label) return 1;
return 0;
@ -89,7 +89,7 @@ export default class LanguageDropdown extends React.Component {
const options = displayedLanguages.map((language) => {
return <div key={language.value}>
{language.label}
{ language.label }
</div>;
});
@ -108,8 +108,8 @@ export default class LanguageDropdown extends React.Component {
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
searchEnabled={true} value={value}
>
{options}
</Dropdown>
{ options }
</Dropdown>;
}
}

View file

@ -96,12 +96,12 @@ module.exports = React.createClass({
// Transform into consecutive repetitions of the same transition (like 5
// consecutive 'joined_and_left's)
const coalescedTransitions = this._coalesceRepeatedTransitions(
canonicalTransitions
canonicalTransitions,
);
const descs = coalescedTransitions.map((t) => {
return this._getDescriptionForTransition(
t.transitionType, plural, t.repeats
t.transitionType, plural, t.repeats,
);
});
@ -119,7 +119,7 @@ module.exports = React.createClass({
return (
<span className="mx_TextualEvent mx_MemberEventListSummary_summary">
<EmojiText>
{summaries.join(", ")}
{ summaries.join(", ") }
</EmojiText>
</span>
);
@ -370,7 +370,7 @@ module.exports = React.createClass({
*/
_renderCommaSeparatedList(items, itemLimit) {
const remaining = itemLimit === undefined ? 0 : Math.max(
items.length - itemLimit, 0
items.length - itemLimit, 0,
);
if (items.length === 0) {
return "";
@ -394,8 +394,8 @@ module.exports = React.createClass({
);
});
return (
<span className="mx_MemberEventListSummary_avatars" onClick={ this._toggleSummary }>
{avatars}
<span className="mx_MemberEventListSummary_avatars" onClick={this._toggleSummary}>
{ avatars }
</span>
);
},
@ -419,19 +419,15 @@ module.exports = React.createClass({
case 'join':
if (e.mxEvent.getPrevContent().membership === 'join') {
if (e.mxEvent.getContent().displayname !==
e.mxEvent.getPrevContent().displayname)
{
e.mxEvent.getPrevContent().displayname) {
return 'changed_name';
}
else if (e.mxEvent.getContent().avatar_url !==
e.mxEvent.getPrevContent().avatar_url)
{
} else if (e.mxEvent.getContent().avatar_url !==
e.mxEvent.getPrevContent().avatar_url) {
return 'changed_avatar';
}
// console.log("MELS ignoring duplicate membership join event");
return null;
}
else {
} else {
return 'joined';
}
case 'leave':
@ -483,7 +479,7 @@ module.exports = React.createClass({
firstEvent.index < aggregateIndices[seq]) {
aggregateIndices[seq] = firstEvent.index;
}
}
},
);
return {
@ -494,7 +490,7 @@ module.exports = React.createClass({
render: function() {
const eventsToRender = this.props.events;
const eventIds = eventsToRender.map(e => e.getId()).join(',');
const eventIds = eventsToRender.map((e) => e.getId()).join(',');
const fewEvents = eventsToRender.length < this.props.threshold;
const expanded = this.state.expanded || fewEvents;
@ -506,7 +502,7 @@ module.exports = React.createClass({
if (fewEvents) {
return (
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
{expandedEvents}
{ expandedEvents }
</div>
);
}
@ -542,7 +538,7 @@ module.exports = React.createClass({
// Sort types by order of lowest event index within sequence
const orderedTransitionSequences = Object.keys(aggregate.names).sort(
(seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2]
(seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2],
);
let summaryContainer = null;
@ -550,24 +546,24 @@ module.exports = React.createClass({
summaryContainer = (
<div className="mx_EventTile_line">
<div className="mx_EventTile_info">
{this._renderAvatars(avatarMembers)}
{this._renderSummary(aggregate.names, orderedTransitionSequences)}
{ this._renderAvatars(avatarMembers) }
{ this._renderSummary(aggregate.names, orderedTransitionSequences) }
</div>
</div>
);
}
const toggleButton = (
<div className={"mx_MemberEventListSummary_toggle"} onClick={this._toggleSummary}>
{expanded ? 'collapse' : 'expand'}
{ expanded ? 'collapse' : 'expand' }
</div>
);
return (
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
{toggleButton}
{summaryContainer}
{expanded ? <div className="mx_MemberEventListSummary_line">&nbsp;</div> : null}
{expandedEvents}
{ toggleButton }
{ summaryContainer }
{ expanded ? <div className="mx_MemberEventListSummary_line">&nbsp;</div> : null }
{ expandedEvents }
</div>
);
},

View file

@ -20,8 +20,8 @@ import React from 'react';
import * as Roles from '../../../Roles';
import { _t } from '../../../languageHandler';
var LEVEL_ROLE_MAP = {};
var reverseRoles = {};
let LEVEL_ROLE_MAP = {};
const reverseRoles = {};
module.exports = React.createClass({
displayName: 'PowerSelector',
@ -46,7 +46,7 @@ module.exports = React.createClass({
custom: (LEVEL_ROLE_MAP[this.props.value] === undefined),
};
},
componentWillMount: function() {
LEVEL_ROLE_MAP = Roles.levelRoleMap();
Object.keys(LEVEL_ROLE_MAP).forEach(function(key) {
@ -72,7 +72,7 @@ module.exports = React.createClass({
},
getValue: function() {
var value;
let value;
if (this.refs.select) {
value = reverseRoles[this.refs.select.value];
if (this.refs.custom) {
@ -83,30 +83,27 @@ module.exports = React.createClass({
},
render: function() {
var customPicker;
let customPicker;
if (this.state.custom) {
var input;
let input;
if (this.props.disabled) {
input = <span>{ this.props.value }</span>;
}
else {
input = <input ref="custom" type="text" size="3" defaultValue={ this.props.value } onBlur={ this.onCustomBlur } onKeyDown={ this.onCustomKeyDown }/>;
} else {
input = <input ref="custom" type="text" size="3" defaultValue={this.props.value} onBlur={this.onCustomBlur} onKeyDown={this.onCustomKeyDown} />;
}
customPicker = <span> of { input }</span>;
}
var selectValue;
let selectValue;
if (this.state.custom) {
selectValue = "Custom";
}
else {
} else {
selectValue = LEVEL_ROLE_MAP[this.props.value] || "Custom";
}
var select;
let select;
if (this.props.disabled) {
select = <span>{ selectValue }</span>;
}
else {
} else {
// Each level must have a definition in LEVEL_ROLE_MAP
const levels = [0, 50, 100];
let options = levels.map((level) => {
@ -115,18 +112,18 @@ module.exports = React.createClass({
// Give a userDefault (users_default in the power event) of 0 but
// because level !== undefined, this should never be used.
text: Roles.textualPowerLevel(level, 0),
}
};
});
options.push({ value: "Custom", text: _t("Custom level") });
options = options.map((op) => {
return <option value={op.value} key={op.value}>{op.text}</option>;
return <option value={op.value} key={op.value}>{ op.text }</option>;
});
select =
<select ref="select"
value={ this.props.controlled ? selectValue : undefined }
defaultValue={ !this.props.controlled ? selectValue : undefined }
onChange={ this.onSelectChange }>
value={this.props.controlled ? selectValue : undefined}
defaultValue={!this.props.controlled ? selectValue : undefined}
onChange={this.onSelectChange}>
{ options }
</select>;
}
@ -137,5 +134,5 @@ module.exports = React.createClass({
{ customPicker }
</span>
);
}
},
});

View file

@ -16,23 +16,23 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
module.exports = React.createClass({
displayName: 'ProgressBar',
propTypes: {
value: React.PropTypes.number,
max: React.PropTypes.number
max: React.PropTypes.number,
},
render: function() {
// Would use an HTML5 progress tag but if that doesn't animate if you
// use the HTML attributes rather than styles
var progressStyle = {
width: ((this.props.value / this.props.max) * 100)+"%"
const progressStyle = {
width: ((this.props.value / this.props.max) * 100)+"%",
};
return (
<div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
);
}
},
});

View file

@ -24,7 +24,7 @@ const RoomDirectoryButton = function(props) {
return (
<ActionButton action="view_room_directory"
mouseOverAction={props.callout ? "callout_room_directory" : null}
label={ _t("Room directory") }
label={_t("Room directory")}
iconPath="img/icons-directory.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -23,7 +23,7 @@ const SettingsButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
<ActionButton action="view_user_settings"
label={ _t("Settings") }
label={_t("Settings")}
iconPath="img/icons-settings.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -24,7 +24,7 @@ const StartChatButton = function(props) {
return (
<ActionButton action="view_create_chat"
mouseOverAction={props.callout ? "callout_start_chat" : null}
label={ _t("Start chat") }
label={_t("Start chat")}
iconPath="img/icons-people.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -16,9 +16,9 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require("react-dom");
var Tinter = require("../../../Tinter");
const React = require('react');
const ReactDOM = require("react-dom");
const Tinter = require("../../../Tinter");
var TintableSvg = React.createClass({
displayName: 'TintableSvg',
@ -63,16 +63,16 @@ var TintableSvg = React.createClass({
render: function() {
return (
<object className={ "mx_TintableSvg " + (this.props.className ? this.props.className : "") }
<object className={"mx_TintableSvg " + (this.props.className ? this.props.className : "")}
type="image/svg+xml"
data={ this.props.src }
width={ this.props.width }
height={ this.props.height }
onLoad={ this.onLoad }
data={this.props.src}
width={this.props.width}
height={this.props.height}
onLoad={this.onLoad}
tabIndex="-1"
/>
);
}
},
});
// Register with the Tinter so that we will be told if the tint changes

View file

@ -52,19 +52,19 @@ module.exports = React.createClass({
},
render: function() {
var self = this;
const self = this;
return (
<div>
<ul className="mx_UserSelector_UserIdList" ref="list">
{this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
})}
{ this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
}) }
</ul>
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")}/>
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} />
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">
{_t("Add User")}
{ _t("Add User") }
</button>
</div>
);
}
},
});

View file

@ -17,6 +17,7 @@ import React from 'react';
import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import { groupRoomFromApiObject } from '../../../groups';
import GroupStoreCache from '../../../stores/GroupStoreCache';
import GeminiScrollbar from 'react-gemini-scrollbar';
import PropTypes from 'prop-types';
import {MatrixClient} from 'matrix-js-sdk';
@ -34,7 +35,6 @@ export default React.createClass({
getInitialState: function() {
return {
fetching: false,
rooms: null,
truncateAt: INITIAL_LOAD_NUM_ROOMS,
searchQuery: "",
@ -43,21 +43,29 @@ export default React.createClass({
componentWillMount: function() {
this._unmounted = false;
this._initGroupStore(this.props.groupId);
},
_initGroupStore: function(groupId) {
this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
this._groupStore.on('update', () => {
this._fetchRooms();
});
this._groupStore.on('error', (err) => {
console.error('Error in group store (listened to by GroupRoomList)', err);
this.setState({
rooms: null,
});
});
this._fetchRooms();
},
_fetchRooms: function() {
this.setState({fetching: true});
this.context.matrixClient.getGroupRooms(this.props.groupId).then((result) => {
this.setState({
rooms: result.chunk.map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
}),
fetching: false,
});
}).catch((e) => {
this.setState({fetching: false});
console.error("Failed to get group room list: ", e);
if (this._unmounted) return;
this.setState({
rooms: this._groupStore.getGroupRooms().map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
}),
});
},
@ -110,12 +118,7 @@ export default React.createClass({
},
render: function() {
if (this.state.fetching) {
const Spinner = sdk.getComponent("elements.Spinner");
return (<div className="mx_GroupRoomList">
<Spinner />
</div>);
} else if (this.state.rooms === null) {
if (this.state.rooms === null) {
return null;
}

View file

@ -21,6 +21,8 @@ import PropTypes from 'prop-types';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import { GroupRoomType } from '../../../groups';
import GroupStoreCache from '../../../stores/GroupStoreCache';
import Modal from '../../../Modal';
const GroupRoomTile = React.createClass({
displayName: 'GroupRoomTile',
@ -31,7 +33,35 @@ const GroupRoomTile = React.createClass({
},
getInitialState: function() {
return {};
return {
name: this.calculateRoomName(this.props.groupRoom),
};
},
componentWillReceiveProps: function(newProps) {
this.setState({
name: this.calculateRoomName(newProps.groupRoom),
});
},
calculateRoomName: function(groupRoom) {
return groupRoom.name || groupRoom.canonicalAlias || _t("Unnamed Room");
},
removeRoomFromGroup: function() {
const groupId = this.props.groupId;
const groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
const roomName = this.state.name;
const roomId = this.props.groupRoom.roomId;
groupStore.removeRoomFromGroup(roomId)
.catch((err) => {
console.error(`Error whilst removing ${roomId} from ${groupId}`, err);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to remove room from group', '', ErrorDialog, {
title: _t("Failed to remove room from group"),
description: _t("Failed to remove '%(roomName)s' from %(groupId)s", {groupId, roomName}),
});
});
},
onClick: function(e) {
@ -49,20 +79,34 @@ const GroupRoomTile = React.createClass({
});
},
onDeleteClick: function(e) {
const groupId = this.props.groupId;
const roomName = this.state.name;
e.preventDefault();
e.stopPropagation();
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Confirm removal of group from room', '', QuestionDialog, {
title: _t("Are you sure you want to remove '%(roomName)s' from %(groupId)s?", {roomName, groupId}),
description: _t("Removing a room from the group will also remove it from the group page."),
button: _t("Remove"),
onFinished: (success) => {
if (success) {
this.removeRoomFromGroup();
}
},
});
},
render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const name = this.props.groupRoom.name ||
this.props.groupRoom.canonicalAlias ||
_t("Unnamed Room");
const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
this.props.groupRoom.avatarUrl,
36, 36, 'crop',
);
const av = (
<BaseAvatar name={name}
<BaseAvatar name={this.state.name}
width={36} height={36}
url={avatarUrl}
/>
@ -74,8 +118,11 @@ const GroupRoomTile = React.createClass({
{ av }
</div>
<div className="mx_GroupRoomTile_name">
{ name }
{ this.state.name }
</div>
<AccessibleButton className="mx_GroupRoomTile_delete" onClick={this.onDeleteClick}>
<img src="img/cancel-small.svg" />
</AccessibleButton>
</AccessibleButton>
);
},

View file

@ -20,7 +20,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { _t, _tJsx } from '../../../languageHandler';
var DIV_ID = 'mx_recaptcha';
const DIV_ID = 'mx_recaptcha';
/**
* A pure UI component which displays a captcha form.
@ -60,9 +60,9 @@ module.exports = React.createClass({
} else {
console.log("Loading recaptcha script...");
window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();};
var protocol = global.location.protocol;
const protocol = global.location.protocol;
if (protocol === "file:") {
var warning = document.createElement('div');
const warning = document.createElement('div');
// XXX: fix hardcoded app URL. Better solutions include:
// * jumping straight to a hosted captcha page (but we don't support that yet)
// * embedding the captcha in an iframe (if that works)
@ -72,11 +72,10 @@ module.exports = React.createClass({
/<a>(.*?)<\/a>/,
(sub) => { return <a href='https://riot.im/app'>{ sub }</a>; }), warning);
this.refs.recaptchaContainer.appendChild(warning);
}
else {
var scriptTag = document.createElement('script');
} else {
const scriptTag = document.createElement('script');
scriptTag.setAttribute(
'src', protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit"
'src', protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit",
);
this.refs.recaptchaContainer.appendChild(scriptTag);
}
@ -93,7 +92,7 @@ module.exports = React.createClass({
throw new Error("Recaptcha did not load successfully");
}
var publicKey = this.props.sitePublicKey;
const publicKey = this.props.sitePublicKey;
if (!publicKey) {
console.error("No public key for recaptcha!");
throw new Error(
@ -130,18 +129,18 @@ module.exports = React.createClass({
if (this.state.errorText) {
error = (
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
);
}
return (
<div ref="recaptchaContainer">
{_t("This Home Server would like to make sure you are not a robot")}
<br/>
{ _t("This Home Server would like to make sure you are not a robot") }
<br />
<div id={DIV_ID}></div>
{error}
{ error }
</div>
);
}
},
});

View file

@ -29,9 +29,9 @@ module.exports = React.createClass({
render: function() {
return (
<div>
<button onClick={this.props.onSubmit}>{_t("Sign in with CAS")}</button>
<button onClick={this.props.onSubmit}>{ _t("Sign in with CAS") }</button>
</div>
);
}
},
});

View file

@ -69,7 +69,7 @@ export default class CountryDropdown extends React.Component {
}
_flagImgForIso2(iso2) {
return <img src={`flags/${iso2}.png`}/>;
return <img src={`flags/${iso2}.png`} />;
}
_getShortOption(iso2) {
@ -111,8 +111,8 @@ export default class CountryDropdown extends React.Component {
const options = displayedCountries.map((country) => {
return <div key={country.iso2}>
{this._flagImgForIso2(country.iso2)}
{country.name} <span>(+{country.prefix})</span>
{ this._flagImgForIso2(country.iso2) }
{ country.name } <span>(+{ country.prefix })</span>
</div>;
});
@ -123,9 +123,9 @@ export default class CountryDropdown extends React.Component {
return <Dropdown className={this.props.className + " left_aligned"}
onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange}
menuWidth={298} getShortOption={this._getShortOption}
value={value} searchEnabled={true}
value={value} searchEnabled={true} disabled={this.props.disabled}
>
{options}
{ options }
</Dropdown>;
}
}
@ -137,4 +137,5 @@ CountryDropdown.propTypes = {
showPrefix: React.PropTypes.bool,
onOptionChange: React.PropTypes.func.isRequired,
value: React.PropTypes.string,
disabled: React.PropTypes.bool,
};

View file

@ -24,27 +24,27 @@ module.exports = React.createClass({
return (
<div className="mx_ErrorDialog">
<div className="mx_Dialog_title">
{_t("Custom Server Options")}
{ _t("Custom Server Options") }
</div>
<div className="mx_Dialog_content">
<span>
{_t("You can use the custom server options to sign into other Matrix " +
"servers by specifying a different Home server URL.")}
<br/>
{_t("This allows you to use this app with an existing Matrix account on " +
"a different home server.")}
<br/>
<br/>
{_t("You can also set a custom identity server but this will typically prevent " +
"interaction with users based on email address.")}
{ _t("You can use the custom server options to sign into other Matrix " +
"servers by specifying a different Home server URL.") }
<br />
{ _t("This allows you to use this app with an existing Matrix account on " +
"a different home server.") }
<br />
<br />
{ _t("You can also set a custom identity server but this will typically prevent " +
"interaction with users based on email address.") }
</span>
</div>
<div className="mx_Dialog_buttons">
<button onClick={this.props.onFinished} autoFocus={true}>
{_t("Dismiss")}
{ _t("Dismiss") }
</button>
</div>
</div>
);
}
},
});

View file

@ -129,8 +129,8 @@ export const PasswordAuthEntry = React.createClass({
return (
<div>
<p>{_t("To continue, please enter your password.")}</p>
<p>{_t("Password:")}</p>
<p>{ _t("To continue, please enter your password.") }</p>
<p>{ _t("Password:") }</p>
<form onSubmit={this._onSubmit}>
<input
ref="passwordField"
@ -139,11 +139,11 @@ export const PasswordAuthEntry = React.createClass({
type="password"
/>
<div className="mx_button_row">
{submitButtonOrSpinner}
{ submitButtonOrSpinner }
</div>
</form>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -178,14 +178,14 @@ export const RecaptchaAuthEntry = React.createClass({
}
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
var sitePublicKey = this.props.stageParams.public_key;
const sitePublicKey = this.props.stageParams.public_key;
return (
<div>
<CaptchaForm sitePublicKey={sitePublicKey}
onCaptchaResponse={this._onCaptchaResponse}
/>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -256,8 +256,8 @@ export const EmailIdentityAuthEntry = React.createClass({
} else {
return (
<div>
<p>{_t("An email has been sent to")} <i>{this.props.inputs.emailAddress}</i></p>
<p>{_t("Please check your email to continue registration.")}</p>
<p>{ _t("An email has been sent to") } <i>{ this.props.inputs.emailAddress }</i></p>
<p>{ _t("Please check your email to continue registration.") }</p>
</div>
);
}
@ -333,12 +333,12 @@ export const MsisdnAuthEntry = React.createClass({
});
this.props.matrixClient.submitMsisdnToken(
this._sid, this.props.clientSecret, this.state.token
this._sid, this.props.clientSecret, this.state.token,
).then((result) => {
if (result.success) {
const idServerParsedUrl = url.parse(
this.props.matrixClient.getIdentityServerUrl(),
)
);
this.props.submitAuthDict({
type: MsisdnAuthEntry.LOGIN_TYPE,
threepid_creds: {
@ -370,8 +370,8 @@ export const MsisdnAuthEntry = React.createClass({
});
return (
<div>
<p>{_t("A text message has been sent to")} +<i>{this._msisdn}</i></p>
<p>{_t("Please enter the code it contains:")}</p>
<p>{ _t("A text message has been sent to") } +<i>{ this._msisdn }</i></p>
<p>{ _t("Please enter the code it contains:") }</p>
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
<form onSubmit={this._onFormSubmit}>
<input type="text"
@ -386,7 +386,7 @@ export const MsisdnAuthEntry = React.createClass({
/>
</form>
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
</div>
</div>
@ -421,9 +421,9 @@ export const FallbackAuthEntry = React.createClass({
},
_onShowFallbackClick: function() {
var url = this.props.matrixClient.getFallbackAuthUrl(
const url = this.props.matrixClient.getFallbackAuthUrl(
this.props.loginType,
this.props.authSessionId
this.props.authSessionId,
);
this._popupWindow = window.open(url);
},
@ -440,9 +440,9 @@ export const FallbackAuthEntry = React.createClass({
render: function() {
return (
<div>
<a onClick={this._onShowFallbackClick}>{_t("Start authentication")}</a>
<a onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -457,7 +457,7 @@ const AuthEntryComponents = [
];
export function getEntryComponentForLoginType(loginType) {
for (var c of AuthEntryComponents) {
for (const c of AuthEntryComponents) {
if (c.LOGIN_TYPE == loginType) {
return c;
}

View file

@ -16,7 +16,7 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
module.exports = React.createClass({
displayName: 'LoginHeader',
@ -27,5 +27,5 @@ module.exports = React.createClass({
Matrix
</div>
);
}
},
});

View file

@ -94,7 +94,7 @@ class PasswordLogin extends React.Component {
onLoginTypeChange(loginType) {
this.setState({
loginType: loginType,
username: "" // Reset because email and username use the same state
username: "", // Reset because email and username use the same state
});
}
@ -116,11 +116,17 @@ class PasswordLogin extends React.Component {
this.props.onPasswordChanged(ev.target.value);
}
renderLoginField(loginType) {
renderLoginField(loginType, disabled) {
const classes = {
mx_Login_field: true,
mx_Login_field_disabled: disabled,
};
switch(loginType) {
case PasswordLogin.LOGIN_FIELD_EMAIL:
classes.mx_Login_email = true;
return <input
className="mx_Login_field mx_Login_email"
className={classNames(classes)}
key="email_input"
type="text"
name="username" // make it a little easier for browser's remember-password
@ -128,10 +134,12 @@ class PasswordLogin extends React.Component {
placeholder="joe@example.com"
value={this.state.username}
autoFocus
disabled={disabled}
/>;
case PasswordLogin.LOGIN_FIELD_MXID:
classes.mx_Login_username = true;
return <input
className="mx_Login_field mx_Login_username"
className={classNames(classes)}
key="username_input"
type="text"
name="username" // make it a little easier for browser's remember-password
@ -139,9 +147,12 @@ class PasswordLogin extends React.Component {
placeholder={_t('User name')}
value={this.state.username}
autoFocus
disabled={disabled}
/>;
case PasswordLogin.LOGIN_FIELD_PHONE:
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
classes.mx_Login_phoneNumberField = true;
classes.mx_Login_field_has_prefix = true;
return <div className="mx_Login_phoneSection">
<CountryDropdown
className="mx_Login_phoneCountry mx_Login_field_prefix"
@ -150,9 +161,10 @@ class PasswordLogin extends React.Component {
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
disabled={disabled}
/>
<input
className="mx_Login_phoneNumberField mx_Login_field mx_Login_field_has_prefix"
className={classNames(classes)}
ref="phoneNumber"
key="phone_input"
type="text"
@ -161,13 +173,14 @@ class PasswordLogin extends React.Component {
placeholder={_t("Mobile phone number")}
value={this.state.phoneNumber}
autoFocus
disabled={disabled}
/>
</div>;
}
}
render() {
var forgotPasswordJsx;
let forgotPasswordJsx;
if (this.props.onForgotPasswordClick) {
forgotPasswordJsx = (
@ -177,14 +190,25 @@ class PasswordLogin extends React.Component {
);
}
let matrixIdText = '';
if (this.props.hsUrl) {
try {
const parsedHsUrl = new URL(this.props.hsUrl);
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
} catch (e) {
// pass
}
}
const pwFieldClass = classNames({
mx_Login_field: true,
mx_Login_field_disabled: matrixIdText === '',
error: this.props.loginIncorrect,
});
const Dropdown = sdk.getComponent('elements.Dropdown');
const loginField = this.renderLoginField(this.state.loginType);
const loginField = this.renderLoginField(this.state.loginType, matrixIdText === '');
return (
<div>
@ -194,20 +218,23 @@ class PasswordLogin extends React.Component {
<Dropdown
className="mx_Login_type_dropdown"
value={this.state.loginType}
disabled={matrixIdText === ''}
onOptionChange={this.onLoginTypeChange}>
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('my Matrix ID') }</span>
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ matrixIdText }</span>
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
</Dropdown>
</div>
{loginField}
{ loginField }
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
name="password"
value={this.state.password} onChange={this.onPasswordChanged}
placeholder={ _t('Password') } />
placeholder={_t('Password')}
disabled={matrixIdText === ''}
/>
<br />
{forgotPasswordJsx}
<input className="mx_Login_submit" type="submit" value={ _t('Sign in') } />
{ forgotPasswordJsx }
<input className="mx_Login_submit" type="submit" value={_t('Sign in')} disabled={matrixIdText === ''} />
</form>
</div>
);

View file

@ -64,7 +64,7 @@ module.exports = React.createClass({
minPasswordLength: 6,
onError: function(e) {
console.error(e);
}
},
};
},
@ -91,16 +91,16 @@ module.exports = React.createClass({
this.validateField(FIELD_PHONE_NUMBER);
this.validateField(FIELD_EMAIL);
var self = this;
const self = this;
if (this.allFieldsValid()) {
if (this.refs.email.value == '') {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, {
title: _t("Warning!"),
description:
<div>
{_t("If you don't specify an email address, you won't be able to reset your password. " +
"Are you sure?")}
{ _t("If you don't specify an email address, you won't be able to reset your password. " +
"Are you sure?") }
</div>,
button: _t("Continue"),
onFinished: function(confirmed) {
@ -116,8 +116,8 @@ module.exports = React.createClass({
},
_doSubmit: function(ev) {
let email = this.refs.email.value.trim();
var promise = this.props.onRegisterClick({
const email = this.refs.email.value.trim();
const promise = this.props.onRegisterClick({
username: this.refs.username.value.trim(),
password: this.refs.password.value.trim(),
email: email,
@ -138,8 +138,8 @@ module.exports = React.createClass({
* they were validated.
*/
allFieldsValid: function() {
var keys = Object.keys(this.state.fieldValid);
for (var i = 0; i < keys.length; ++i) {
const keys = Object.keys(this.state.fieldValid);
for (let i = 0; i < keys.length; ++i) {
if (this.state.fieldValid[keys[i]] == false) {
return false;
}
@ -152,8 +152,8 @@ module.exports = React.createClass({
},
validateField: function(field_id) {
var pwd1 = this.refs.password.value.trim();
var pwd2 = this.refs.passwordConfirm.value.trim();
const pwd1 = this.refs.password.value.trim();
const pwd2 = this.refs.passwordConfirm.value.trim();
switch (field_id) {
case FIELD_EMAIL:
@ -162,7 +162,7 @@ module.exports = React.createClass({
const matchingTeam = this.props.teamsConfig.teams.find(
(team) => {
return email.split('@').pop() === team.domain;
}
},
) || null;
this.setState({
selectedTeam: matchingTeam,
@ -191,13 +191,13 @@ module.exports = React.createClass({
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_USERNAME_INVALID"
"RegistrationForm.ERR_USERNAME_INVALID",
);
} else if (username == '') {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_USERNAME_BLANK"
"RegistrationForm.ERR_USERNAME_BLANK",
);
} else {
this.markFieldValid(field_id, true);
@ -208,13 +208,13 @@ module.exports = React.createClass({
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_PASSWORD_MISSING"
"RegistrationForm.ERR_PASSWORD_MISSING",
);
} else if (pwd1.length < this.props.minPasswordLength) {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_PASSWORD_LENGTH"
"RegistrationForm.ERR_PASSWORD_LENGTH",
);
} else {
this.markFieldValid(field_id, true);
@ -223,14 +223,14 @@ module.exports = React.createClass({
case FIELD_PASSWORD_CONFIRM:
this.markFieldValid(
field_id, pwd1 == pwd2,
"RegistrationForm.ERR_PASSWORD_MISMATCH"
"RegistrationForm.ERR_PASSWORD_MISMATCH",
);
break;
}
},
markFieldValid: function(field_id, val, error_code) {
var fieldValid = this.state.fieldValid;
const fieldValid = this.state.fieldValid;
fieldValid[field_id] = val;
this.setState({fieldValid: fieldValid});
if (!val) {
@ -271,7 +271,7 @@ module.exports = React.createClass({
},
render: function() {
var self = this;
const self = this;
const emailSection = (
<div>
@ -280,7 +280,7 @@ module.exports = React.createClass({
defaultValue={this.props.defaultEmail}
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_EMAIL);}}
value={self.state.email}/>
value={self.state.email} />
</div>
);
let belowEmailSection;
@ -291,7 +291,7 @@ module.exports = React.createClass({
Sorry, but your university is not registered with us just yet.&nbsp;
Email us on&nbsp;
<a href={"mailto:" + this.props.teamsConfig.supportEmail}>
{this.props.teamsConfig.supportEmail}
{ this.props.teamsConfig.supportEmail }
</a>&nbsp;
to get your university signed up. Or continue to register with Riot to enjoy our open source platform.
</p>
@ -299,7 +299,7 @@ module.exports = React.createClass({
} else if (this.state.selectedTeam) {
belowEmailSection = (
<p className="mx_Login_support">
{_t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name})}
{ _t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name}) }
</p>
);
}
@ -321,7 +321,7 @@ module.exports = React.createClass({
FIELD_PHONE_NUMBER,
'mx_Login_phoneNumberField',
'mx_Login_field',
'mx_Login_field_has_prefix'
'mx_Login_field_has_prefix',
)}
onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}}
value={self.state.phoneNumber}
@ -333,16 +333,16 @@ module.exports = React.createClass({
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
);
let placeholderUserName = _t("User name");
const placeholderUserName = _t("User name");
return (
<div>
<form onSubmit={this.onSubmit}>
{emailSection}
{belowEmailSection}
{phoneSection}
{ emailSection }
{ belowEmailSection }
{ phoneSection }
<input type="text" ref="username"
placeholder={ placeholderUserName } defaultValue={this.props.defaultUsername}
placeholder={placeholderUserName} defaultValue={this.props.defaultUsername}
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
<br />
@ -357,9 +357,9 @@ module.exports = React.createClass({
onBlur={function() {self.validateField(FIELD_PASSWORD_CONFIRM);}}
defaultValue={this.props.defaultPassword} />
<br />
{registerButton}
{ registerButton }
</form>
</div>
);
}
},
});

View file

@ -16,9 +16,9 @@ limitations under the License.
'use strict';
var React = require('react');
var Modal = require('../../../Modal');
var sdk = require('../../../index');
const React = require('react');
const Modal = require('../../../Modal');
const sdk = require('../../../index');
import { _t } from '../../../languageHandler';
/**
@ -45,7 +45,7 @@ module.exports = React.createClass({
customIsUrl: React.PropTypes.string,
withToggleButton: React.PropTypes.bool,
delayTimeMs: React.PropTypes.number // time to wait before invoking onChanged
delayTimeMs: React.PropTypes.number, // time to wait before invoking onChanged
},
getDefaultProps: function() {
@ -54,7 +54,7 @@ module.exports = React.createClass({
customHsUrl: "",
customIsUrl: "",
withToggleButton: false,
delayTimeMs: 0
delayTimeMs: 0,
};
},
@ -65,18 +65,18 @@ module.exports = React.createClass({
// if withToggleButton is false, then show the config all the time given we have no way otherwise of making it visible
configVisible: !this.props.withToggleButton ||
(this.props.customHsUrl !== this.props.defaultHsUrl) ||
(this.props.customIsUrl !== this.props.defaultIsUrl)
(this.props.customIsUrl !== this.props.defaultIsUrl),
};
},
onHomeserverChanged: function(ev) {
this.setState({hs_url: ev.target.value}, function() {
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, function() {
var hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
let hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
this.props.onServerConfigChange({
hsUrl : this.state.hs_url,
isUrl : this.state.is_url,
hsUrl: this.state.hs_url,
isUrl: this.state.is_url,
});
});
});
@ -85,11 +85,11 @@ module.exports = React.createClass({
onIdentityServerChanged: function(ev) {
this.setState({is_url: ev.target.value}, function() {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, function() {
var isUrl = this.state.is_url.trim().replace(/\/$/, "");
let isUrl = this.state.is_url.trim().replace(/\/$/, "");
if (isUrl === "") isUrl = this.props.defaultIsUrl;
this.props.onServerConfigChange({
hsUrl : this.state.hs_url,
isUrl : this.state.is_url,
hsUrl: this.state.hs_url,
isUrl: this.state.is_url,
});
});
});
@ -104,32 +104,31 @@ module.exports = React.createClass({
onServerConfigVisibleChange: function(visible, ev) {
this.setState({
configVisible: visible
configVisible: visible,
});
if (!visible) {
this.props.onServerConfigChange({
hsUrl : this.props.defaultHsUrl,
isUrl : this.props.defaultIsUrl,
hsUrl: this.props.defaultHsUrl,
isUrl: this.props.defaultIsUrl,
});
}
else {
} else {
this.props.onServerConfigChange({
hsUrl : this.state.hs_url,
isUrl : this.state.is_url,
hsUrl: this.state.hs_url,
isUrl: this.state.is_url,
});
}
},
showHelpPopup: function() {
var CustomServerDialog = sdk.getComponent('login.CustomServerDialog');
const CustomServerDialog = sdk.getComponent('login.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
},
render: function() {
var serverConfigStyle = {};
const serverConfigStyle = {};
serverConfigStyle.display = this.state.configVisible ? 'block' : 'none';
var toggleButton;
let toggleButton;
if (this.props.withToggleButton) {
toggleButton = (
<div className="mx_ServerConfig_selector">
@ -137,14 +136,14 @@ module.exports = React.createClass({
checked={!this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, false)} />
<label className="mx_Login_label" htmlFor="basic">
{_t("Default server")}
{ _t("Default server") }
</label>
&nbsp;&nbsp;
<input className="mx_Login_radio" id="advanced" name="configVisible" type="radio"
checked={this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, true)} />
<label className="mx_Login_label" htmlFor="advanced">
{_t("Custom server")}
{ _t("Custom server") }
</label>
</div>
);
@ -152,11 +151,11 @@ module.exports = React.createClass({
return (
<div>
{toggleButton}
{ toggleButton }
<div style={serverConfigStyle}>
<div className="mx_ServerConfig">
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">
{_t("Home server URL")}
{ _t("Home server URL") }
</label>
<input className="mx_Login_field" id="hsurl" type="text"
placeholder={this.props.defaultHsUrl}
@ -164,7 +163,7 @@ module.exports = React.createClass({
value={this.state.hs_url}
onChange={this.onHomeserverChanged} />
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">
{_t("Identity server URL")}
{ _t("Identity server URL") }
</label>
<input className="mx_Login_field" id="isurl" type="text"
placeholder={this.props.defaultIsUrl}
@ -172,11 +171,11 @@ module.exports = React.createClass({
value={this.state.is_url}
onChange={this.onIdentityServerChanged} />
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
{_t("What does this mean?")}
{ _t("What does this mean?") }
</a>
</div>
</div>
</div>
);
}
},
});

View file

@ -35,7 +35,7 @@ export default class MAudioBody extends React.Component {
}
onPlayToggle() {
this.setState({
playing: !this.state.playing
playing: !this.state.playing,
});
}
@ -49,9 +49,9 @@ export default class MAudioBody extends React.Component {
}
componentDidMount() {
var content = this.props.mxEvent.getContent();
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
var decryptedBlob;
let decryptedBlob;
decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
return readBlobAsDataUri(decryptedBlob);
@ -70,14 +70,13 @@ export default class MAudioBody extends React.Component {
}
render() {
const content = this.props.mxEvent.getContent();
if (this.state.error !== null) {
return (
<span className="mx_MAudioBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting audio")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting audio") }
</span>
);
}
@ -89,7 +88,7 @@ export default class MAudioBody extends React.Component {
// Not sure how tall the audio player is so not sure how tall it should actually be.
return (
<span className="mx_MAudioBody">
<img src="img/spinner.gif" alt={content.body} width="16" height="16"/>
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
</span>
);
}

View file

@ -28,10 +28,10 @@ import Modal from '../../../Modal';
// A cached tinted copy of "img/download.svg"
var tintedDownloadImageURL;
let tintedDownloadImageURL;
// Track a list of mounted MFileBody instances so that we can update
// the "img/download.svg" when the tint changes.
var nextMountId = 0;
let nextMountId = 0;
const mounts = {};
/**
@ -169,11 +169,11 @@ function computedStyle(element) {
return "";
}
const style = window.getComputedStyle(element, null);
var cssText = style.cssText;
let cssText = style.cssText;
if (cssText == "") {
// Firefox doesn't implement ".cssText" for computed styles.
// https://bugzilla.mozilla.org/show_bug.cgi?id=137687
for (var i = 0; i < style.length; i++) {
for (let i = 0; i < style.length; i++) {
cssText += style[i] + ":";
cssText += style.getPropertyValue(style[i]) + ";";
}
@ -202,7 +202,7 @@ module.exports = React.createClass({
* @return {string} the human readable link text for the attachment.
*/
presentableTextForFile: function(content) {
var linkText = _t("Attachment");
let linkText = _t("Attachment");
if (content.body && content.body.length > 0) {
// The content body should be the name of the file including a
// file extension.
@ -270,7 +270,7 @@ module.exports = React.createClass({
// Need to decrypt the attachment
// Wait for the user to click on the link before downloading
// and decrypting the attachment.
var decrypting = false;
let decrypting = false;
const decrypt = () => {
if (decrypting) {
return false;
@ -328,14 +328,14 @@ module.exports = React.createClass({
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<div style={{display: "none"}}>
{/*
{ /*
* Add dummy copy of the "a" tag
* We'll use it to learn how the download link
* would have been styled if it was rendered inline.
*/}
<a ref="dummyLink"/>
*/ }
<a ref="dummyLink" />
</div>
<iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe"/>
<iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe" />
</div>
</span>
);
@ -356,13 +356,12 @@ module.exports = React.createClass({
</div>
</span>
);
}
else {
} else {
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<a href={contentUrl} download={fileName} target="_blank" rel="noopener">
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage"/>
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage" />
{ _t("Download %(text)s", { text: text }) }
</a>
</div>
@ -370,7 +369,7 @@ module.exports = React.createClass({
);
}
} else {
var extra = text ? (': ' + text) : '';
const extra = text ? (': ' + text) : '';
return <span className="mx_MFileBody">
{ _t("Invalid file%(extra)s", { extra: extra }) }
</span>;

View file

@ -191,8 +191,8 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
<span className="mx_MImageBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting image")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting image") }
</span>
);
}
@ -210,7 +210,7 @@ module.exports = React.createClass({
}}>
<img src="img/spinner.gif" alt={content.body} width="32" height="32" style={{
"margin": "auto",
}}/>
}} />
</div>
</span>
);
@ -227,7 +227,7 @@ module.exports = React.createClass({
if (thumbUrl) {
return (
<span className="mx_MImageBody" ref="body">
<a href={contentUrl} onClick={ this.onClick }>
<a href={contentUrl} onClick={this.onClick}>
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
alt={content.body}
onMouseEnter={this.onImageEnter}
@ -239,13 +239,13 @@ module.exports = React.createClass({
} else if (content.body) {
return (
<span className="mx_MImageBody">
{_t("Image '%(Body)s' cannot be displayed.", {Body: content.body})}
{ _t("Image '%(Body)s' cannot be displayed.", {Body: content.body}) }
</span>
);
} else {
return (
<span className="mx_MImageBody">
{_t("This image cannot be displayed.")}
{ _t("This image cannot be displayed.") }
</span>
);
}

View file

@ -54,13 +54,12 @@ module.exports = React.createClass({
// no scaling needs to be applied
return 1;
}
var widthMulti = thumbWidth / fullWidth;
var heightMulti = thumbHeight / fullHeight;
const widthMulti = thumbWidth / fullWidth;
const heightMulti = thumbHeight / fullHeight;
if (widthMulti < heightMulti) {
// width is the dominant dimension so scaling will be fixed on that
return widthMulti;
}
else {
} else {
// height is the dominant dimension so scaling will be fixed on that
return heightMulti;
}
@ -89,15 +88,15 @@ module.exports = React.createClass({
componentDidMount: function() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
var thumbnailPromise = Promise.resolve(null);
let thumbnailPromise = Promise.resolve(null);
if (content.info.thumbnail_file) {
thumbnailPromise = decryptFile(
content.info.thumbnail_file
content.info.thumbnail_file,
).then(function(blob) {
return readBlobAsDataUri(blob);
});
}
var decryptedBlob;
let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
@ -126,8 +125,8 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
<span className="mx_MVideoBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting video")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting video") }
</span>
);
}
@ -144,7 +143,7 @@ module.exports = React.createClass({
"justify-items": "center",
"width": "100%",
}}>
<img src="img/spinner.gif" alt={content.body} width="16" height="16"/>
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
</div>
</span>
);

Some files were not shown because too many files have changed in this diff Show more