Merge branch 'develop' into luke/fix-room-list-group-store-leak

This commit is contained in:
lukebarnard 2018-01-02 16:19:37 +00:00
commit 874a7bf1de
8 changed files with 68 additions and 33 deletions

View file

@ -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);
}

View file

@ -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
}

View file

@ -1068,7 +1068,7 @@ export default React.createClass({
if (!self._loggedInView) {
return true;
}
return self._loggedInView.canResetTimelineInRoom(roomId);
return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId);
});
cli.on('sync', function(state, prevState) {

View file

@ -854,9 +854,13 @@ module.exports = React.createClass({
ev.dataTransfer.dropEffect = 'none';
const items = ev.dataTransfer.items;
if (items.length == 1) {
if (items[0].kind == 'file') {
const items = [...ev.dataTransfer.items];
if (items.length >= 1) {
const isDraggingFiles = items.every(function(item) {
return item.kind == 'file';
});
if (isDraggingFiles) {
this.setState({ draggingFile: true });
ev.dataTransfer.dropEffect = 'copy';
}
@ -867,10 +871,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) {

View file

@ -374,7 +374,7 @@ export const MsisdnAuthEntry = React.createClass({
return (
<div>
<p>{ _t("A text message has been sent to %(msisdn)s",
{ msisdn: <i>this._msisdn</i> },
{ msisdn: <i>{ this._msisdn }</i> },
) }
</p>
<p>{ _t("Please enter the code it contains:") }</p>

View file

@ -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) : '',
// TODO: Namespace themes through some standard
'$theme': SettingsStore.getValue("theme"),
};
app.id = appId;

View file

@ -89,11 +89,11 @@ module.exports = React.createClass({
this._groupStoreTokens = [];
// 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) => {
@ -109,7 +109,7 @@ module.exports = React.createClass({
);
});
// Filters themselves have changed, refresh the selected tags
this.updateSelectedTagsRooms(dmRoomMap, FilterStore.getSelectedTags());
this.updateVisibleRooms();
});
this.refreshRoomList();
@ -276,29 +276,30 @@ module.exports = React.createClass({
this.refreshRoomList();
}, 500),
// Update which rooms and users should appear in RoomList as dictated by selected tags
updateSelectedTagsRooms: function(dmRoomMap, updatedTags) {
// Update which rooms and users should appear in RoomList for a given group tag
updateVisibleRoomsForTag: 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._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._visibleRoomsForGroup[tag].push(roomId),
);
});
// TODO: Check if room has been tagged to the group by the user
},
this._selectedTagsRoomIds = [];
// Update which rooms and users should appear according to which tags are selected
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),
);
});
@ -311,7 +312,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() {

View file

@ -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 <span className='translated-string' data-orig-string={text}>{substituted}</span>;
}
}
else {
return substituted;
}
}
/*