diff --git a/.travis-test-riot.sh b/.travis-test-riot.sh index 876ce207f3..7f2660a906 100755 --- a/.travis-test-riot.sh +++ b/.travis-test-riot.sh @@ -34,5 +34,5 @@ ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web # PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh # CHROME_PATH=$(which google-chrome-stable) ./run.sh ./install.sh -./run.sh +./run.sh --travis popd diff --git a/package.json b/package.json index 2cbdb98c2e..effa135d72 100644 --- a/package.json +++ b/package.json @@ -85,15 +85,15 @@ "react-dom": "^15.6.0", "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "resize-observer-polyfill": "^1.5.0", + "sanitize-html": "^1.18.4", "slate": "0.34.7", - "slate-react": "^0.12.4", "slate-html-serializer": "^0.6.1", "slate-md-serializer": "matrix-org/slate-md-serializer#f7c4ad3", - "sanitize-html": "^1.18.4", + "slate-react": "^0.12.4", "text-encoding-utf-8": "^1.0.1", "url": "^0.11.0", "velocity-vector": "vector-im/velocity#059e3b2", - "whatwg-fetch": "^1.0.0" + "whatwg-fetch": "^1.1.1" }, "devDependencies": { "babel-cli": "^6.5.2", diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index fa9ccc8ed7..c35cd3466a 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -143,7 +143,7 @@ function getTranslationsJs(file) { // Validate tag replacements if (node.arguments.length > 2) { const tagMap = node.arguments[2]; - for (const prop of tagMap.properties) { + for (const prop of tagMap.properties || []) { if (prop.key.type === 'Literal') { const tag = prop.key.value; // RegExp same as in src/languageHandler.js diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 15c67526d9..6b68941ee3 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -198,6 +198,48 @@ function textForMessageEvent(ev) { return message; } +function textForRoomAliasesEvent(ev) { + // An alternative implementation of this as a first-class event can be found at + // https://github.com/matrix-org/matrix-react-sdk/blob/dc7212ec2bd12e1917233ed7153b3e0ef529a135/src/components/views/messages/RoomAliasesEvent.js + // This feels a bit overkill though, and it's not clear the i18n really needs it + // so instead it's landing as a simple textual event. + + const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); + const oldAliases = ev.getPrevContent().aliases || []; + const newAliases = ev.getContent().aliases || []; + + const addedAliases = newAliases.filter((x) => !oldAliases.includes(x)); + const removedAliases = oldAliases.filter((x) => !newAliases.includes(x)); + + if (!addedAliases.length && !removedAliases.length) { + return ''; + } + + if (addedAliases.length && !removedAliases.length) { + return _t('%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.', { + senderName: senderName, + count: addedAliases.length, + addedAddresses: addedAliases.join(', '), + }); + } else if (!addedAliases.length && removedAliases.length) { + return _t('%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.', { + senderName: senderName, + count: removedAliases.length, + removedAddresses: removedAliases.join(', '), + }); + } else { + const args = { + senderName: senderName, + addedAddresses: addedAliases.join(', '), + removedAddresses: removedAliases.join(', '), + }; + return _t( + '%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.', + args, + ); + } +} + function textForCallAnswerEvent(event) { const senderName = event.sender ? event.sender.name : _t('Someone'); const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); @@ -359,6 +401,7 @@ const handlers = { }; const stateHandlers = { + 'm.room.aliases': textForRoomAliasesEvent, 'm.room.name': textForRoomNameEvent, 'm.room.topic': textForTopicEvent, 'm.room.member': textForMemberEvent, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 8dcc2139a7..dd93171e90 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -47,6 +47,8 @@ const eventTileTypes = { }; const stateEventTileTypes = { + 'm.room.aliases': 'messages.TextualEvent', + // 'm.room.aliases': 'messages.RoomAliasesEvent', // too complex 'm.room.create': 'messages.RoomCreate', 'm.room.member': 'messages.TextualEvent', 'm.room.name': 'messages.TextualEvent', @@ -58,7 +60,6 @@ const stateEventTileTypes = { 'm.room.power_levels': 'messages.TextualEvent', 'm.room.pinned_events': 'messages.TextualEvent', 'm.room.server_acl': 'messages.TextualEvent', - 'im.vector.modular.widgets': 'messages.TextualEvent', }; @@ -484,7 +485,9 @@ module.exports = withMatrixClient(React.createClass({ const eventType = this.props.mxEvent.getType(); // Info messages are basically information about commands processed on a room - const isInfoMessage = (eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType != 'm.room.create'); + const isInfoMessage = ( + eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType != 'm.room.create' + ); const tileHandler = getHandlerTile(this.props.mxEvent); // This shouldn't happen: the caller should check we support this type diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index d73f9b5ccf..54044e8d65 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -273,6 +273,7 @@ module.exports = React.createClass({ }); let name = this.state.roomName; + if (name == undefined || name == null) name = ''; name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon let badgeContent; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4643d4bdff..b974e0a96c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1246,6 +1246,11 @@ "Import room keys": "Import room keys", "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", + "%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s added %(addedAddresses)s as an address for this room.", + "%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s added %(addedAddresses)s as addresses for this room.", + "%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s removed %(removedAddresses)s as an address for this room.", + "%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s removed %(removedAddresses)s as addresses for this room.", + "%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.", "File to import": "File to import", "Import": "Import", "Failed to set direct chat tag": "Failed to set direct chat tag",