From 713230e0824b6c50b7ecb73971f051a4479d8b2a Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Sat, 9 Dec 2017 14:22:20 +0100 Subject: [PATCH 01/15] Add option to also output original string This is useful for debugging and testing Signed-off-by: Stefan Parviainen --- src/languageHandler.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index 59d71505a4..e732927a75 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -23,6 +23,10 @@ import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; const i18nFolder = 'i18n/'; +// Control whether to also return original, untranslated strings +// Useful for debugging and testing +const ANNOTATE_STRINGS = false; + // We use english strings as keys, some of which contain full stops counterpart.setSeparator('|'); // Fall back to English @@ -84,7 +88,21 @@ export function _t(text, variables, tags) { // The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution) const translated = safeCounterpartTranslate(text, args); - return substitute(translated, variables, tags); + let substituted = substitute(translated, variables, tags); + + // For development/testing purposes it is useful to also output the original string + // Don't do that for release versions + if (ANNOTATE_STRINGS) { + if (typeof substituted === 'string') { + return `@@${text}##${substituted}@@` + } + else { + return {substituted}; + } + } + else { + return substituted; + } } /* From 961d5e1868275712a649f5ffe29a0179112cb694 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 15 Dec 2017 14:23:35 +0000 Subject: [PATCH 02/15] Split updatedSelectedTagsRooms because we don't need to get the rooms for all groups when the filtered tags change. --- src/components/views/rooms/RoomList.js | 33 +++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index d94fef7d31..ec13f6d535 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -102,11 +102,12 @@ module.exports = React.createClass({ this._groupStores[tag] = GroupStoreCache.getGroupStore(tag); this._groupStores[tag].registerListener(() => { // This group's rooms or members may have updated, update rooms for its tag - this.updateSelectedTagsRooms(dmRoomMap, [tag]); + this.updateSelectedTagsRoomsForGroups(dmRoomMap, tag); + this.updateSelectedTagsRooms(); }); }); // Filters themselves have changed, refresh the selected tags - this.updateSelectedTagsRooms(dmRoomMap, FilterStore.getSelectedTags()); + this.updateSelectedTagsRooms(); }); this.refreshRoomList(); @@ -269,24 +270,24 @@ module.exports = React.createClass({ }, 500), // Update which rooms and users should appear in RoomList as dictated by selected tags - updateSelectedTagsRooms: function(dmRoomMap, updatedTags) { + updateSelectedTagsRoomsForGroups: function(dmRoomMap, tag) { if (!this.mounted) return; - updatedTags.forEach((tag) => { - // For now, only handle group tags - const store = this._groupStores[tag]; - if (!store) return; + // For now, only handle group tags + const store = this._groupStores[tag]; + if (!store) return; - this._selectedTagsRoomIdsForGroup[tag] = []; - store.getGroupRooms().forEach((room) => this._selectedTagsRoomIdsForGroup[tag].push(room.roomId)); - store.getGroupMembers().forEach((member) => { - if (member.userId === MatrixClientPeg.get().credentials.userId) return; - dmRoomMap.getDMRoomsForUserId(member.userId).forEach( - (roomId) => this._selectedTagsRoomIdsForGroup[tag].push(roomId), - ); - }); - // TODO: Check if room has been tagged to the group by the user + this._selectedTagsRoomIdsForGroup[tag] = []; + store.getGroupRooms().forEach((room) => this._selectedTagsRoomIdsForGroup[tag].push(room.roomId)); + store.getGroupMembers().forEach((member) => { + if (member.userId === MatrixClientPeg.get().credentials.userId) return; + dmRoomMap.getDMRoomsForUserId(member.userId).forEach( + (roomId) => this._selectedTagsRoomIdsForGroup[tag].push(roomId), + ); }); + // TODO: Check if room has been tagged to the group by the user + }, + updateSelectedTagsRooms: function() { this._selectedTagsRoomIds = []; FilterStore.getSelectedTags().forEach((tag) => { (this._selectedTagsRoomIdsForGroup[tag] || []).forEach( From 653137f6ec56063a77f543088cea767c08e99c82 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 15 Dec 2017 17:21:20 +0000 Subject: [PATCH 03/15] Update comment --- src/components/views/rooms/RoomList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index ec13f6d535..85a076aa48 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -269,7 +269,7 @@ module.exports = React.createClass({ this.refreshRoomList(); }, 500), - // Update which rooms and users should appear in RoomList as dictated by selected tags + // Update which rooms and users should appear in RoomList for a given group tag updateSelectedTagsRoomsForGroups: function(dmRoomMap, tag) { if (!this.mounted) return; // For now, only handle group tags From 56affd3d42b9432a804c95cbcbb8b643b06a7b2a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 15 Dec 2017 17:29:06 +0000 Subject: [PATCH 04/15] Rename for clarity --- src/components/views/rooms/RoomList.js | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 85a076aa48..1fba2070ba 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -88,11 +88,11 @@ module.exports = React.createClass({ this._groupStores = {}; // A map between tags which are group IDs and the room IDs of rooms that should be kept // in the room list when filtering by that tag. - this._selectedTagsRoomIdsForGroup = { + this._visibleRoomsForGroup = { // $groupId: [$roomId1, $roomId2, ...], }; // All rooms that should be kept in the room list when filtering - this._selectedTagsRoomIds = []; + this._visibleRooms = []; // When the selected tags are changed, initialise a group store if necessary this._filterStoreToken = FilterStore.addListener(() => { FilterStore.getSelectedTags().forEach((tag) => { @@ -102,12 +102,12 @@ module.exports = React.createClass({ this._groupStores[tag] = GroupStoreCache.getGroupStore(tag); this._groupStores[tag].registerListener(() => { // This group's rooms or members may have updated, update rooms for its tag - this.updateSelectedTagsRoomsForGroups(dmRoomMap, tag); - this.updateSelectedTagsRooms(); + this.updateVisibleRoomsForTag(dmRoomMap, tag); + this.updateVisibleRooms(); }); }); // Filters themselves have changed, refresh the selected tags - this.updateSelectedTagsRooms(); + this.updateVisibleRooms(); }); this.refreshRoomList(); @@ -270,28 +270,28 @@ module.exports = React.createClass({ }, 500), // Update which rooms and users should appear in RoomList for a given group tag - updateSelectedTagsRoomsForGroups: function(dmRoomMap, tag) { + updateVisibleRoomsForTag: function(dmRoomMap, tag) { if (!this.mounted) return; // For now, only handle group tags const store = this._groupStores[tag]; if (!store) return; - this._selectedTagsRoomIdsForGroup[tag] = []; - store.getGroupRooms().forEach((room) => this._selectedTagsRoomIdsForGroup[tag].push(room.roomId)); + this._visibleRoomsForGroup[tag] = []; + store.getGroupRooms().forEach((room) => this._visibleRoomsForGroup[tag].push(room.roomId)); store.getGroupMembers().forEach((member) => { if (member.userId === MatrixClientPeg.get().credentials.userId) return; dmRoomMap.getDMRoomsForUserId(member.userId).forEach( - (roomId) => this._selectedTagsRoomIdsForGroup[tag].push(roomId), + (roomId) => this._visibleRoomsForGroup[tag].push(roomId), ); }); // TODO: Check if room has been tagged to the group by the user }, - updateSelectedTagsRooms: function() { - this._selectedTagsRoomIds = []; + updateVisibleRooms: function() { + this._visibleRooms = []; FilterStore.getSelectedTags().forEach((tag) => { - (this._selectedTagsRoomIdsForGroup[tag] || []).forEach( - (roomId) => this._selectedTagsRoomIds.push(roomId), + (this._visibleRoomsForGroup[tag] || []).forEach( + (roomId) => this._visibleRooms.push(roomId), ); }); @@ -304,7 +304,7 @@ module.exports = React.createClass({ isRoomInSelectedTags: function(room) { // No selected tags = every room is visible in the list - return this.state.selectedTags.length === 0 || this._selectedTagsRoomIds.includes(room.roomId); + return this.state.selectedTags.length === 0 || this._visibleRooms.includes(room.roomId); }, refreshRoomList: function() { From d292dab9c6350530cff81cd057339074ebf1ca0b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 15 Dec 2017 17:30:21 +0000 Subject: [PATCH 05/15] More doc --- src/components/views/rooms/RoomList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 1fba2070ba..b305b04fd0 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -287,6 +287,7 @@ module.exports = React.createClass({ // TODO: Check if room has been tagged to the group by the user }, + // Update which rooms and users should appear according to which tags are selected updateVisibleRooms: function() { this._visibleRooms = []; FilterStore.getSelectedTags().forEach((tag) => { From 94ef80c0088e8bf2bc815306b136a620df4de371 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 16 Dec 2017 20:11:00 -0700 Subject: [PATCH 06/15] Support sending the current theme to widgets This is a namespaced variable because some clients may not be able to support themes, or may have varying definitions of what "light" means. Widgets are recommended to opt for per-client checks, or accept that some clients may differ. Signed-off-by: Travis Ralston --- src/components/views/rooms/AppsDrawer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 78677e138b..365dc57777 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -27,6 +27,7 @@ import ScalarAuthClient from '../../../ScalarAuthClient'; import ScalarMessaging from '../../../ScalarMessaging'; import { _t } from '../../../languageHandler'; import WidgetUtils from '../../../WidgetUtils'; +import SettingsStore from "../../../settings/SettingsStore"; // The maximum number of widgets that can be added in a room const MAX_WIDGETS = 2; @@ -131,6 +132,9 @@ module.exports = React.createClass({ '$matrix_room_id': this.props.room.roomId, '$matrix_display_name': user ? user.displayName : this.props.userId, '$matrix_avatar_url': user ? MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl) : '', + + // Namespaced for Riot + '$riot_theme': SettingsStore.getValue("theme"), }; app.id = appId; From 678d87ef206d9ea52a5bd5f6ea046c2228c12ce2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 16 Dec 2017 20:13:27 -0700 Subject: [PATCH 07/15] Give the current theme to the integration manager For integration managers which would like to theme themselves to match Riot. Signed-off-by: Travis Ralston --- src/ScalarAuthClient.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 3e775a94ab..7bd8603264 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -15,6 +15,7 @@ limitations under the License. */ import Promise from 'bluebird'; +import SettingsStore from "./settings/SettingsStore"; const request = require('browser-request'); const SdkConfig = require('./SdkConfig'); @@ -109,6 +110,7 @@ class ScalarAuthClient { let url = SdkConfig.get().integrations_ui_url; url += "?scalar_token=" + encodeURIComponent(this.scalarToken); url += "&room_id=" + encodeURIComponent(roomId); + url += "&theme=" + encodeURIComponent(SettingsStore.getValue("theme")); if (id) { url += '&integ_id=' + encodeURIComponent(id); } From fddbf3a2a2790a25ef60c95535a66caac5089e34 Mon Sep 17 00:00:00 2001 From: Alexandr Korsak Date: Mon, 18 Dec 2017 12:23:54 +0300 Subject: [PATCH 08/15] Fix issue with preview of phone number on register and waiting for sms code confirmation code --- src/components/views/login/InteractiveAuthEntryComponents.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index d0b6c8decb..ae2fb54c2d 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -374,7 +374,7 @@ export const MsisdnAuthEntry = React.createClass({ return (

{ _t("A text message has been sent to %(msisdn)s", - { msisdn: this._msisdn }, + { msisdn: { this._msisdn } }, ) }

{ _t("Please enter the code it contains:") }

From c07c4fd1cb9670d81472963c943dbc61e3535ccd Mon Sep 17 00:00:00 2001 From: Alexandr Korsak Date: Mon, 18 Dec 2017 13:20:18 +0300 Subject: [PATCH 09/15] Allow multiple file uploads on drag & drop --- src/components/structures/RoomView.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 138c110c4f..287509d962 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -45,6 +45,8 @@ import RoomViewStore from '../../stores/RoomViewStore'; import RoomScrollStateStore from '../../stores/RoomScrollStateStore'; import SettingsStore from "../../settings/SettingsStore"; +import _every from "lodash/every"; + const DEBUG = false; let debuglog = function() {}; @@ -855,8 +857,12 @@ module.exports = React.createClass({ ev.dataTransfer.dropEffect = 'none'; const items = ev.dataTransfer.items; - if (items.length == 1) { - if (items[0].kind == 'file') { + if (items.length >= 1) { + const isDraggingFiles = _every(items, function(item) { + return item.kind == 'file'; + }); + + if (isDraggingFiles) { this.setState({ draggingFile: true }); ev.dataTransfer.dropEffect = 'copy'; } @@ -867,10 +873,8 @@ module.exports = React.createClass({ ev.stopPropagation(); ev.preventDefault(); this.setState({ draggingFile: false }); - const files = ev.dataTransfer.files; - if (files.length == 1) { - this.uploadFile(files[0]); - } + const files = [...ev.dataTransfer.files]; + files.forEach(this.uploadFile); }, onDragLeaveOrEnd: function(ev) { From 78af02b29b6ac127573caeb2c4b2897521f41146 Mon Sep 17 00:00:00 2001 From: Alexandr Korsak Date: Mon, 18 Dec 2017 15:51:01 +0300 Subject: [PATCH 10/15] Replace lodash/every by Array.prototype.every function --- src/components/structures/RoomView.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 287509d962..e240ab38d5 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -45,8 +45,6 @@ import RoomViewStore from '../../stores/RoomViewStore'; import RoomScrollStateStore from '../../stores/RoomScrollStateStore'; import SettingsStore from "../../settings/SettingsStore"; -import _every from "lodash/every"; - const DEBUG = false; let debuglog = function() {}; @@ -856,9 +854,9 @@ module.exports = React.createClass({ ev.dataTransfer.dropEffect = 'none'; - const items = ev.dataTransfer.items; + const items = [...ev.dataTransfer.items]; if (items.length >= 1) { - const isDraggingFiles = _every(items, function(item) { + const isDraggingFiles = items.every(function(item) { return item.kind == 'file'; }); From d051a2e0b399e19dfa21bb8b499f2d748ace8fcc Mon Sep 17 00:00:00 2001 From: Alexandr Korsak Date: Mon, 18 Dec 2017 15:59:21 +0300 Subject: [PATCH 11/15] Remove extra space --- src/components/views/login/InteractiveAuthEntryComponents.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index ae2fb54c2d..e8997b4112 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -374,7 +374,7 @@ export const MsisdnAuthEntry = React.createClass({ return (

{ _t("A text message has been sent to %(msisdn)s", - { msisdn: { this._msisdn } }, + { msisdn: { this._msisdn } }, ) }

{ _t("Please enter the code it contains:") }

From e5df0dcf1efc4c0b07f54b999d8cc99773d2b72d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 18 Dec 2017 09:19:54 -0700 Subject: [PATCH 12/15] $riot_theme -> $theme Signed-off-by: Travis Ralston --- src/components/views/rooms/AppsDrawer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 365dc57777..e29f46fd37 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -133,8 +133,8 @@ module.exports = React.createClass({ '$matrix_display_name': user ? user.displayName : this.props.userId, '$matrix_avatar_url': user ? MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl) : '', - // Namespaced for Riot - '$riot_theme': SettingsStore.getValue("theme"), + // TODO: Namespace themes through some standard + '$theme': SettingsStore.getValue("theme"), }; app.id = appId; From e65c67ca153758de8803aed515b2d36ac2759e93 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Dec 2017 22:47:18 +0000 Subject: [PATCH 13/15] fix https://github.com/vector-im/riot-web/issues/5841 --- src/components/structures/MatrixChat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 3ac2f5bd50..7ee18364df 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1065,10 +1065,10 @@ export default React.createClass({ // this if we are not scrolled up in the view. To find out, delegate to // the timeline panel. If the timeline panel doesn't exist, then we assume // it is safe to reset the timeline. - if (!self._loggedInView) { + if (!self._loggedInView || !self._loggedInView.child) { return true; } - return self._loggedInView.canResetTimelineInRoom(roomId); + return self._loggedInView.child.canResetTimelineInRoom(roomId); }); cli.on('sync', function(state, prevState) { From fa14bc9b8d2252f9d891d4eb54168fb0a272cd1c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Dec 2017 22:49:23 +0000 Subject: [PATCH 14/15] don't gutwrench --- src/components/structures/MatrixChat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7ee18364df..3452d13841 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1065,10 +1065,10 @@ export default React.createClass({ // this if we are not scrolled up in the view. To find out, delegate to // the timeline panel. If the timeline panel doesn't exist, then we assume // it is safe to reset the timeline. - if (!self._loggedInView || !self._loggedInView.child) { + if (!self._loggedInView) { return true; } - return self._loggedInView.child.canResetTimelineInRoom(roomId); + return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId); }); cli.on('sync', function(state, prevState) { From b5f6d97fa2fcb9ca002889a719bf282d83e5145e Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 19 Dec 2017 15:39:13 +0000 Subject: [PATCH 15/15] Ignore postmessage requests with api field set. --- src/ScalarMessaging.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 7bde607451..3c164c6551 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -557,8 +557,16 @@ const onMessage = function(event) { // // All strings start with the empty string, so for sanity return if the length // of the event origin is 0. + // + // TODO -- Scalar postMessage API should be namespaced with event.data.api field + // Fix following "if" statement to respond only to specific API messages. const url = SdkConfig.get().integrations_ui_url; - if (event.origin.length === 0 || !url.startsWith(event.origin) || !event.data.action) { + if ( + event.origin.length === 0 || + !url.startsWith(event.origin) || + !event.data.action || + event.data.api // Ignore messages with specific API set + ) { return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise }