mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 13:14:58 +08:00
Merge branch 'develop' into luke/fix-room-list-group-store-leak
This commit is contained in:
commit
874a7bf1de
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user