Merge remote-tracking branch 'upstream/develop' into feature-autocomplete
commit
cd928fe6f5
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react",
|
||||
"flowtype"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"amd": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"rules": {
|
||||
"no-undef": ["warn"],
|
||||
"global-strict": ["off"],
|
||||
"no-extra-semi": ["warn"],
|
||||
"no-underscore-dangle": ["off"],
|
||||
"no-console": ["off"],
|
||||
"no-unused-vars": ["off"],
|
||||
"no-trailing-spaces": ["warn", {
|
||||
"skipBlankLines": true
|
||||
}],
|
||||
"no-unreachable": ["warn"],
|
||||
"no-spaced-func": ["warn"],
|
||||
"no-new-func": ["error"],
|
||||
"no-new-wrappers": ["error"],
|
||||
"no-invalid-regexp": ["error"],
|
||||
"no-extra-bind": ["error"],
|
||||
"no-magic-numbers": ["error"],
|
||||
"consistent-return": ["error"],
|
||||
"valid-jsdoc": ["error"],
|
||||
"no-use-before-define": ["error"],
|
||||
"camelcase": ["warn"],
|
||||
"array-callback-return": ["error"],
|
||||
"dot-location": ["warn", "property"],
|
||||
"guard-for-in": ["error"],
|
||||
"no-useless-call": ["warn"],
|
||||
"no-useless-escape": ["warn"],
|
||||
"no-useless-concat": ["warn"],
|
||||
"brace-style": ["warn", "1tbs"],
|
||||
"comma-style": ["warn", "last"],
|
||||
"space-before-function-paren": ["warn", "never"],
|
||||
"space-before-blocks": ["warn", "always"],
|
||||
"keyword-spacing": ["warn", {
|
||||
"before": true,
|
||||
"after": true
|
||||
}],
|
||||
|
||||
// dangling commas required, but only for multiline objects/arrays
|
||||
"comma-dangle": ["warn", "always-multiline"],
|
||||
// always === instead of ==, unless dealing with null/undefined
|
||||
"eqeqeq": ["error", "smart"],
|
||||
// always use curly braces, even with single statements
|
||||
"curly": ["error", "all"],
|
||||
// phasing out var in favour of let/const is a good idea
|
||||
"no-var": ["warn"],
|
||||
// always require semicolons
|
||||
"semi": ["error", "always"],
|
||||
// prefer rest and spread over the Old Ways
|
||||
"prefer-spread": ["warn"],
|
||||
"prefer-rest-params": ["warn"],
|
||||
|
||||
/** react **/
|
||||
|
||||
// bind or arrow function in props causes performance issues
|
||||
"react/jsx-no-bind": ["error"],
|
||||
"react/jsx-key": ["error"],
|
||||
"react/prefer-stateless-function": ["warn"],
|
||||
"react/sort-comp": ["warn"],
|
||||
|
||||
/** flowtype **/
|
||||
"flowtype/require-parameter-type": 1,
|
||||
"flowtype/require-return-type": [
|
||||
1,
|
||||
"always",
|
||||
{
|
||||
"annotateUndefined": "never"
|
||||
}
|
||||
],
|
||||
"flowtype/space-after-type-colon": [
|
||||
1,
|
||||
"always"
|
||||
],
|
||||
"flowtype/space-before-type-colon": [
|
||||
1,
|
||||
"never"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"flowtype": {
|
||||
"onlyFilesWithFlowAnnotation": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
npm-debug.log
|
||||
|
||||
/node_modules
|
||||
/lib
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
set -e
|
||||
|
||||
export KARMAFLAGS="--no-colors"
|
||||
export NVM_DIR="/home/jenkins/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 4
|
||||
|
@ -14,6 +15,9 @@ npm install
|
|||
# run the mocha tests
|
||||
npm run test
|
||||
|
||||
# run eslint
|
||||
npm run lint -- -f checkstyle -o eslint.xml || true
|
||||
|
||||
# delete the old tarball, if it exists
|
||||
rm -f matrix-react-sdk-*.tgz
|
||||
|
||||
|
|
10
package.json
10
package.json
|
@ -16,10 +16,12 @@
|
|||
"reskindex": "reskindex -h header",
|
||||
"build": "babel src -d lib --source-maps",
|
||||
"start": "babel src -w -d lib --source-maps",
|
||||
"lint": "eslint src/",
|
||||
"lintall": "eslint src/ test/",
|
||||
"clean": "rimraf lib",
|
||||
"prepublish": "npm run build && git rev-parse HEAD > git-revision.txt",
|
||||
"test": "karma start --browsers PhantomJS",
|
||||
"test-multi": "karma start --single-run=false"
|
||||
"test": "karma start $KARMAFLAGS --browsers PhantomJS",
|
||||
"test-multi": "karma start $KARMAFLAGS --single-run=false"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.1.2",
|
||||
|
@ -55,8 +57,12 @@
|
|||
"devDependencies": {
|
||||
"babel": "^5.8.23",
|
||||
"babel-core": "^5.8.38",
|
||||
"babel-eslint": "^6.1.0",
|
||||
"babel-loader": "^5.4.0",
|
||||
"babel-polyfill": "^6.5.0",
|
||||
"eslint": "^2.13.1",
|
||||
"eslint-plugin-flowtype": "^2.3.0",
|
||||
"eslint-plugin-react": "^5.2.2",
|
||||
"expect": "^1.16.0",
|
||||
"json-loader": "^0.5.3",
|
||||
"karma": "^0.13.22",
|
||||
|
|
|
@ -24,30 +24,5 @@ module.exports = {
|
|||
getDisplayAliasForRoom: function(room) {
|
||||
return room.getCanonicalAlias() || room.getAliases()[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a list of room objects, return the room which has the given alias,
|
||||
* else null.
|
||||
*/
|
||||
getRoomForAlias: function(rooms, room_alias) {
|
||||
var room;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var aliasEvents = rooms[i].currentState.getStateEvents(
|
||||
"m.room.aliases"
|
||||
);
|
||||
for (var j = 0; j < aliasEvents.length; j++) {
|
||||
var aliases = aliasEvents[j].getContent().aliases || [];
|
||||
for (var k = 0; k < aliases.length; k++) {
|
||||
if (aliases[k] === room_alias) {
|
||||
room = rooms[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (room) { break; }
|
||||
}
|
||||
if (room) { break; }
|
||||
}
|
||||
return room || null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket 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.
|
||||
*/
|
||||
|
||||
var DEFAULTS = {
|
||||
// URL to a page we show in an iframe to configure integrations
|
||||
//integrations_ui_url: "https://scalar.vector.im/",
|
||||
integrations_ui_url: "http://127.0.0.1:5051/",
|
||||
// Base URL to the REST interface of the integrations server
|
||||
//integrations_rest_url: "https://scalar.vector.im/api",
|
||||
integrations_rest_url: "http://127.0.0.1:5050",
|
||||
};
|
||||
|
||||
class SdkConfig {
|
||||
|
||||
static get() {
|
||||
return global.mxReactSdkConfig;
|
||||
}
|
||||
|
||||
static put(cfg) {
|
||||
var defaultKeys = Object.keys(DEFAULTS);
|
||||
for (var i = 0; i < defaultKeys.length; ++i) {
|
||||
if (cfg[defaultKeys[i]] === undefined) {
|
||||
cfg[defaultKeys[i]] = DEFAULTS[defaultKeys[i]];
|
||||
}
|
||||
}
|
||||
global.mxReactSdkConfig = cfg;
|
||||
}
|
||||
|
||||
static unset() {
|
||||
global.mxReactSdkConfig = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SdkConfig;
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var MatrixTools = require("./MatrixTools");
|
||||
var dis = require("./dispatcher");
|
||||
var encryption = require("./encryption");
|
||||
var Tinter = require("./Tinter");
|
||||
|
||||
|
||||
|
@ -82,32 +81,13 @@ var commands = {
|
|||
return success(
|
||||
MatrixClientPeg.get().setRoomAccountData(
|
||||
room_id, "org.matrix.room.color_scheme", colorScheme
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
}),
|
||||
|
||||
encrypt: new Command("encrypt", "<on|off>", function(room_id, args) {
|
||||
if (args == "on") {
|
||||
var client = MatrixClientPeg.get();
|
||||
var members = client.getRoom(room_id).currentState.members;
|
||||
var user_ids = Object.keys(members);
|
||||
return success(
|
||||
encryption.enableEncryption(client, room_id, user_ids)
|
||||
);
|
||||
}
|
||||
if (args == "off") {
|
||||
var client = MatrixClientPeg.get();
|
||||
return success(
|
||||
encryption.disableEncryption(client, room_id)
|
||||
);
|
||||
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
}),
|
||||
|
||||
// Change the room topic
|
||||
topic: new Command("topic", "<topic>", function(room_id, args) {
|
||||
if (args) {
|
||||
|
@ -132,46 +112,25 @@ var commands = {
|
|||
}),
|
||||
|
||||
// Join a room
|
||||
join: new Command("join", "<room_alias>", function(room_id, args) {
|
||||
join: new Command("join", "#alias:domain", function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room_alias = matches[1];
|
||||
if (room_alias[0] !== '#') {
|
||||
return reject("Usage: /join #alias:domain");
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
if (!room_alias.match(/:/)) {
|
||||
room_alias += ':' + MatrixClientPeg.get().getDomain();
|
||||
}
|
||||
|
||||
// Try to find a room with this alias
|
||||
// XXX: do we need to do this? Doesn't the JS SDK suppress duplicate attempts to join the same room?
|
||||
var foundRoom = MatrixTools.getRoomForAlias(
|
||||
MatrixClientPeg.get().getRooms(),
|
||||
room_alias
|
||||
);
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_alias: room_alias,
|
||||
auto_join: true,
|
||||
});
|
||||
|
||||
if (foundRoom) { // we've already joined this room, view it if it's not archived.
|
||||
var me = foundRoom.getMember(MatrixClientPeg.get().credentials.userId);
|
||||
if (me && me.membership !== "leave") {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: foundRoom.roomId
|
||||
});
|
||||
return success();
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise attempt to join this alias.
|
||||
return success(
|
||||
MatrixClientPeg.get().joinRoom(room_alias).then(
|
||||
function(room) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId
|
||||
});
|
||||
})
|
||||
);
|
||||
return success();
|
||||
}
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
|
|
|
@ -24,7 +24,6 @@ var PresetValues = {
|
|||
Custom: "custom",
|
||||
};
|
||||
var q = require('q');
|
||||
var encryption = require("../../encryption");
|
||||
var sdk = require('../../index');
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -108,17 +107,8 @@ module.exports = React.createClass({
|
|||
|
||||
var deferred = cli.createRoom(options);
|
||||
|
||||
var response;
|
||||
|
||||
if (this.state.encrypt) {
|
||||
deferred = deferred.then(function(res) {
|
||||
response = res;
|
||||
return encryption.enableEncryption(
|
||||
cli, response.room_id, options.invite
|
||||
);
|
||||
}).then(function() {
|
||||
return q(response) }
|
||||
);
|
||||
// TODO
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -108,10 +108,14 @@ module.exports = React.createClass({
|
|||
return window.localStorage.getItem("mx_hs_url");
|
||||
}
|
||||
else {
|
||||
return this.props.config.default_hs_url || "https://matrix.org";
|
||||
return this.getDefaultHsUrl();
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultHsUrl() {
|
||||
return this.props.config.default_hs_url || "https://matrix.org";
|
||||
},
|
||||
|
||||
getFallbackHsUrl: function() {
|
||||
return this.props.config.fallback_hs_url;
|
||||
},
|
||||
|
@ -126,10 +130,14 @@ module.exports = React.createClass({
|
|||
return window.localStorage.getItem("mx_is_url");
|
||||
}
|
||||
else {
|
||||
return this.props.config.default_is_url || "https://vector.im"
|
||||
return this.getDefaultIsUrl();
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultIsUrl() {
|
||||
return this.props.config.default_is_url || "https://vector.im";
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.favicon = new Favico({animation: 'none'});
|
||||
},
|
||||
|
@ -151,8 +159,8 @@ module.exports = React.createClass({
|
|||
this.onLoggedIn({
|
||||
userId: this.props.startingQueryParams.guest_user_id,
|
||||
accessToken: this.props.startingQueryParams.guest_access_token,
|
||||
homeserverUrl: this.props.config.default_hs_url,
|
||||
identityServerUrl: this.props.config.default_is_url,
|
||||
homeserverUrl: this.getDefaultHsUrl(),
|
||||
identityServerUrl: this.getDefaultIsUrl(),
|
||||
guest: true
|
||||
});
|
||||
}
|
||||
|
@ -403,10 +411,7 @@ module.exports = React.createClass({
|
|||
// known to be in (eg. user clicks on a room in the recents panel), supply the ID
|
||||
// If the user is clicking on a room in the context of the alias being presented
|
||||
// to them, supply the room alias. If both are supplied, the room ID will be ignored.
|
||||
this._viewRoom(
|
||||
payload.room_id, payload.room_alias, payload.show_settings, payload.event_id,
|
||||
payload.third_party_invite, payload.oob_data
|
||||
);
|
||||
this._viewRoom(payload);
|
||||
break;
|
||||
case 'view_prev_room':
|
||||
roomIndexDelta = -1;
|
||||
|
@ -423,7 +428,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
|
||||
if (roomIndex < 0) roomIndex = allRooms.length - 1;
|
||||
this._viewRoom(allRooms[roomIndex].roomId);
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
break;
|
||||
case 'view_indexed_room':
|
||||
var allRooms = RoomListSorter.mostRecentActivityFirst(
|
||||
|
@ -431,7 +436,7 @@ module.exports = React.createClass({
|
|||
);
|
||||
var roomIndex = payload.roomIndex;
|
||||
if (allRooms[roomIndex]) {
|
||||
this._viewRoom(allRooms[roomIndex].roomId);
|
||||
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
|
||||
}
|
||||
break;
|
||||
case 'view_user_settings':
|
||||
|
@ -491,39 +496,45 @@ module.exports = React.createClass({
|
|||
|
||||
// switch view to the given room
|
||||
//
|
||||
// eventId is optional and will cause a switch to the context of that
|
||||
// particular event.
|
||||
// @param {Object} thirdPartyInvite Object containing data about the third party
|
||||
// @param {Object} room_info Object containing data about the room to be joined
|
||||
// @param {string=} room_info.room_id ID of the room to join. One of room_id or room_alias must be given.
|
||||
// @param {string=} room_info.room_alias Alias of the room to join. One of room_id or room_alias must be given.
|
||||
// @param {boolean=} room_info.auto_join If true, automatically attempt to join the room if not already a member.
|
||||
// @param {boolean=} room_info.show_settings Makes RoomView show the room settings dialog.
|
||||
// @param {string=} room_info.event_id ID of the event in this room to show: this will cause a switch to the
|
||||
// context of that particular event.
|
||||
// @param {Object=} room_info.third_party_invite Object containing data about the third party
|
||||
// we received to join the room, if any.
|
||||
// @param {string} thirdPartyInvite.inviteSignUrl 3pid invite sign URL
|
||||
// @param {string} thirdPartyInvite.invitedwithEmail The email address the invite was sent to
|
||||
// @param {Object} oob_data Object of additional data about the room
|
||||
// @param {string=} room_info.third_party_invite.inviteSignUrl 3pid invite sign URL
|
||||
// @param {string=} room_info.third_party_invite.invitedEmail The email address the invite was sent to
|
||||
// @param {Object=} room_info.oob_data Object of additional data about the room
|
||||
// that has been passed out-of-band (eg.
|
||||
// room name and avatar from an invite email)
|
||||
_viewRoom: function(roomId, roomAlias, showSettings, eventId, thirdPartyInvite, oob_data) {
|
||||
_viewRoom: function(room_info) {
|
||||
// before we switch room, record the scroll state of the current room
|
||||
this._updateScrollMap();
|
||||
|
||||
this.focusComposer = true;
|
||||
|
||||
var newState = {
|
||||
initialEventId: eventId,
|
||||
highlightedEventId: eventId,
|
||||
initialEventId: room_info.event_id,
|
||||
highlightedEventId: room_info.event_id,
|
||||
initialEventPixelOffset: undefined,
|
||||
page_type: this.PageTypes.RoomView,
|
||||
thirdPartyInvite: thirdPartyInvite,
|
||||
roomOobData: oob_data,
|
||||
currentRoomAlias: roomAlias,
|
||||
thirdPartyInvite: room_info.third_party_invite,
|
||||
roomOobData: room_info.oob_data,
|
||||
currentRoomAlias: room_info.room_alias,
|
||||
autoJoin: room_info.auto_join,
|
||||
};
|
||||
|
||||
if (!roomAlias) {
|
||||
newState.currentRoomId = roomId;
|
||||
if (!room_info.room_alias) {
|
||||
newState.currentRoomId = room_info.room_id;
|
||||
}
|
||||
|
||||
// if we aren't given an explicit event id, look for one in the
|
||||
// scrollStateMap.
|
||||
if (!eventId) {
|
||||
var scrollState = this.scrollStateMap[roomId];
|
||||
if (!room_info.event_id) {
|
||||
var scrollState = this.scrollStateMap[room_info.room_id];
|
||||
if (scrollState) {
|
||||
newState.initialEventId = scrollState.focussedEvent;
|
||||
newState.initialEventPixelOffset = scrollState.pixelOffset;
|
||||
|
@ -536,8 +547,8 @@ module.exports = React.createClass({
|
|||
// the new screen yet (we won't be showing it yet)
|
||||
// The normal case where this happens is navigating
|
||||
// to the room in the URL bar on page load.
|
||||
var presentedId = roomAlias || roomId;
|
||||
var room = MatrixClientPeg.get().getRoom(roomId);
|
||||
var presentedId = room_info.room_alias || room_info.room_id;
|
||||
var room = MatrixClientPeg.get().getRoom(room_info.room_id);
|
||||
if (room) {
|
||||
var theAlias = MatrixTools.getDisplayAliasForRoom(room);
|
||||
if (theAlias) presentedId = theAlias;
|
||||
|
@ -553,15 +564,15 @@ module.exports = React.createClass({
|
|||
// Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
||||
}
|
||||
|
||||
if (eventId) {
|
||||
presentedId += "/"+eventId;
|
||||
if (room_info.event_id) {
|
||||
presentedId += "/"+room_info.event_id;
|
||||
}
|
||||
this.notifyNewScreen('room/'+presentedId);
|
||||
newState.ready = true;
|
||||
}
|
||||
this.setState(newState);
|
||||
|
||||
if (this.refs.roomView && showSettings) {
|
||||
if (this.refs.roomView && room_info.showSettings) {
|
||||
this.refs.roomView.showSettings(true);
|
||||
}
|
||||
},
|
||||
|
@ -1030,6 +1041,7 @@ module.exports = React.createClass({
|
|||
<RoomView
|
||||
ref="roomView"
|
||||
roomAddress={this.state.currentRoomAlias || this.state.currentRoomId}
|
||||
autoJoin={this.state.autoJoin}
|
||||
onRoomIdResolved={this.onRoomIdResolved}
|
||||
eventId={this.state.initialEventId}
|
||||
thirdPartyInvite={this.state.thirdPartyInvite}
|
||||
|
@ -1109,8 +1121,8 @@ module.exports = React.createClass({
|
|||
email={this.props.startingQueryParams.email}
|
||||
username={this.state.upgradeUsername}
|
||||
guestAccessToken={this.state.guestAccessToken}
|
||||
defaultHsUrl={this.props.config.default_hs_url}
|
||||
defaultIsUrl={this.props.config.default_is_url}
|
||||
defaultHsUrl={this.getDefaultHsUrl()}
|
||||
defaultIsUrl={this.getDefaultIsUrl()}
|
||||
brand={this.props.config.brand}
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
|
@ -1124,8 +1136,8 @@ module.exports = React.createClass({
|
|||
} else if (this.state.screen == 'forgot_password') {
|
||||
return (
|
||||
<ForgotPassword
|
||||
defaultHsUrl={this.props.config.default_hs_url}
|
||||
defaultIsUrl={this.props.config.default_is_url}
|
||||
defaultHsUrl={this.getDefaultHsUrl()}
|
||||
defaultIsUrl={this.getDefaultIsUrl()}
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
onComplete={this.onLoginClick}
|
||||
|
@ -1136,13 +1148,13 @@ module.exports = React.createClass({
|
|||
<Login
|
||||
onLoggedIn={this.onLoggedIn}
|
||||
onRegisterClick={this.onRegisterClick}
|
||||
defaultHsUrl={this.props.config.default_hs_url}
|
||||
defaultIsUrl={this.props.config.default_is_url}
|
||||
defaultHsUrl={this.getDefaultHsUrl()}
|
||||
defaultIsUrl={this.getDefaultIsUrl()}
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||
onLoginAsGuestClick={this.props.enableGuest && this.props.config && this.props.config.default_hs_url ? this._registerAsGuest.bind(this, true) : undefined}
|
||||
onLoginAsGuestClick={this.props.enableGuest && this.props.config && this._registerAsGuest.bind(this, true)}
|
||||
onCancelClick={ this.state.guestCreds ? this.onReturnToGuestClick : null }
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -119,6 +119,11 @@ module.exports = React.createClass({
|
|||
guestsCanJoin: false,
|
||||
canPeek: false,
|
||||
|
||||
// error object, as from the matrix client/server API
|
||||
// If we failed to load information about the room,
|
||||
// store the error here.
|
||||
roomLoadError: null,
|
||||
|
||||
// this is true if we are fully scrolled-down, and are looking at
|
||||
// the end of the live timeline. It has the effect of hiding the
|
||||
// 'scroll to bottom' knob, among a couple of other things.
|
||||
|
@ -161,10 +166,11 @@ module.exports = React.createClass({
|
|||
roomId: result.room_id,
|
||||
roomLoading: !room,
|
||||
hasUnsentMessages: this._hasUnsentMessages(room),
|
||||
}, this._updatePeeking);
|
||||
}, this._onHaveRoom);
|
||||
}, (err) => {
|
||||
this.setState({
|
||||
roomLoading: false,
|
||||
roomLoadError: err,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
@ -174,11 +180,11 @@ module.exports = React.createClass({
|
|||
room: room,
|
||||
roomLoading: !room,
|
||||
hasUnsentMessages: this._hasUnsentMessages(room),
|
||||
}, this._updatePeeking);
|
||||
}, this._onHaveRoom);
|
||||
}
|
||||
},
|
||||
|
||||
_updatePeeking: function() {
|
||||
_onHaveRoom: function() {
|
||||
// if this is an unknown room then we're in one of three states:
|
||||
// - This is a room we can peek into (search engine) (we can /peek)
|
||||
// - This is a room we can publicly join or were invited to. (we can /join)
|
||||
|
@ -189,29 +195,44 @@ module.exports = React.createClass({
|
|||
// Note that peeking works by room ID and room ID only, as opposed to joining
|
||||
// which must be by alias or invite wherever possible (peeking currently does
|
||||
// not work over federation).
|
||||
if (!this.state.room && this.state.roomId) {
|
||||
console.log("Attempting to peek into room %s", this.state.roomId);
|
||||
|
||||
MatrixClientPeg.get().peekInRoom(this.state.roomId).then((room) => {
|
||||
this.setState({
|
||||
room: room,
|
||||
roomLoading: false,
|
||||
});
|
||||
this._onRoomLoaded(room);
|
||||
}, (err) => {
|
||||
// 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.
|
||||
if (err.errcode == "M_GUEST_ACCESS_FORBIDDEN") {
|
||||
// This is fine: the room just isn't peekable (we assume).
|
||||
// NB. We peek if we are not in the room, although if we try to peek into
|
||||
// a room in which we have a member event (ie. we've left) synapse will just
|
||||
// send us the same data as we get in the sync (ie. the last events we saw).
|
||||
var user_is_in_room = null;
|
||||
if (this.state.room) {
|
||||
user_is_in_room = this.state.room.hasMembershipState(
|
||||
MatrixClientPeg.get().credentials.userId, 'join'
|
||||
);
|
||||
}
|
||||
|
||||
if (!user_is_in_room && this.state.roomId) {
|
||||
if (this.props.autoJoin) {
|
||||
this.onJoinButtonClicked();
|
||||
} else if (this.state.roomId) {
|
||||
console.log("Attempting to peek into room %s", this.state.roomId);
|
||||
|
||||
MatrixClientPeg.get().peekInRoom(this.state.roomId).then((room) => {
|
||||
this.setState({
|
||||
room: room,
|
||||
roomLoading: false,
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}).done();
|
||||
} else if (this.state.room) {
|
||||
this._onRoomLoaded(room);
|
||||
}, (err) => {
|
||||
// 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.
|
||||
if (err.errcode == "M_GUEST_ACCESS_FORBIDDEN") {
|
||||
// This is fine: the room just isn't peekable (we assume).
|
||||
this.setState({
|
||||
roomLoading: false,
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}).done();
|
||||
}
|
||||
} else if (user_is_in_room) {
|
||||
MatrixClientPeg.get().stopPeeking();
|
||||
this._onRoomLoaded(this.state.room);
|
||||
}
|
||||
|
@ -999,7 +1020,7 @@ module.exports = React.createClass({
|
|||
this.setState({
|
||||
rejecting: true
|
||||
});
|
||||
MatrixClientPeg.get().leave(this.props.roomAddress).done(function() {
|
||||
MatrixClientPeg.get().leave(this.state.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
self.setState({
|
||||
rejecting: false
|
||||
|
@ -1274,6 +1295,7 @@ module.exports = React.createClass({
|
|||
|
||||
// We have no room object for this room, only the ID.
|
||||
// We've got to this room by following a link, possibly a third party invite.
|
||||
var room_alias = this.props.roomAddress[0] == '#' ? this.props.roomAddress : null;
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<RoomHeader ref="header"
|
||||
|
@ -1284,7 +1306,8 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomView_auxPanel">
|
||||
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
|
||||
onRejectClick={ this.onRejectThreepidInviteButtonClicked }
|
||||
canJoin={ true } canPreview={ false }
|
||||
canPreview={ false } error={ this.state.roomLoadError }
|
||||
roomAlias={room_alias}
|
||||
spinner={this.state.joining}
|
||||
inviterName={inviterName}
|
||||
invitedEmail={invitedEmail}
|
||||
|
@ -1322,7 +1345,7 @@ module.exports = React.createClass({
|
|||
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
|
||||
onRejectClick={ this.onRejectButtonClicked }
|
||||
inviterName={ inviterName }
|
||||
canJoin={ true } canPreview={ false }
|
||||
canPreview={ false }
|
||||
spinner={this.state.joining}
|
||||
room={this.state.room}
|
||||
/>
|
||||
|
@ -1392,7 +1415,7 @@ module.exports = React.createClass({
|
|||
invitedEmail = this.props.thirdPartyInvite.invitedEmail;
|
||||
}
|
||||
aux = (
|
||||
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked} canJoin={true}
|
||||
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
|
||||
onRejectClick={this.onRejectThreepidInviteButtonClicked}
|
||||
spinner={this.state.joining}
|
||||
inviterName={inviterName}
|
||||
|
|
|
@ -232,7 +232,9 @@ module.exports = React.createClass({displayName: 'Login',
|
|||
<div className="mx_Login_box">
|
||||
<LoginHeader />
|
||||
<div>
|
||||
<h2>Sign in</h2>
|
||||
<h2>Sign in
|
||||
{ loader }
|
||||
</h2>
|
||||
{ this.componentForStep(this._getCurrentFlowStep()) }
|
||||
<ServerConfig ref="serverConfig"
|
||||
withToggleButton={true}
|
||||
|
@ -244,7 +246,6 @@ module.exports = React.createClass({displayName: 'Login',
|
|||
onIsUrlChanged={this.onIsUrlChanged}
|
||||
delayTimeMs={1000}/>
|
||||
<div className="mx_Login_error">
|
||||
{ loader }
|
||||
{ this.state.errorText }
|
||||
</div>
|
||||
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
|
||||
|
|
|
@ -34,10 +34,15 @@ module.exports = React.createClass({
|
|||
|
||||
propTypes: {
|
||||
value: React.PropTypes.number.isRequired,
|
||||
|
||||
// if true, the <select/> should be a 'controlled' form element and updated by React
|
||||
// to reflect the current value, rather than left freeform.
|
||||
// MemberInfo uses controlled; RoomSettings uses non-controlled.
|
||||
controlled: React.PropTypes.bool.isRequired,
|
||||
//
|
||||
// ignored if disabled is truthy. false by default.
|
||||
controlled: React.PropTypes.bool,
|
||||
|
||||
// should the user be able to change the value? false by default.
|
||||
disabled: React.PropTypes.bool,
|
||||
onChange: React.PropTypes.func,
|
||||
},
|
||||
|
|
|
@ -139,7 +139,8 @@ module.exports = React.createClass({
|
|||
|
||||
componentDidMount: function() {
|
||||
this._suppressReadReceiptAnimation = false;
|
||||
MatrixClientPeg.get().on("deviceVerified", this.onDeviceVerified);
|
||||
MatrixClientPeg.get().on("deviceVerificationChanged",
|
||||
this.onDeviceVerificationChanged);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
|
@ -163,11 +164,12 @@ module.exports = React.createClass({
|
|||
componentWillUnmount: function() {
|
||||
var client = MatrixClientPeg.get();
|
||||
if (client) {
|
||||
client.removeListener("deviceVerified", this.onDeviceVerified);
|
||||
client.removeListener("deviceVerificationChanged",
|
||||
this.onDeviceVerificationChanged);
|
||||
}
|
||||
},
|
||||
|
||||
onDeviceVerified: function(userId, device) {
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
if (userId == this.props.mxEvent.getSender()) {
|
||||
this._verifyEvent(this.props.mxEvent);
|
||||
}
|
||||
|
|
|
@ -36,32 +36,73 @@ module.exports = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
onBlockClick: function() {
|
||||
MatrixClientPeg.get().setDeviceBlocked(
|
||||
this.props.userId, this.props.device.id, true
|
||||
);
|
||||
},
|
||||
|
||||
onUnblockClick: function() {
|
||||
MatrixClientPeg.get().setDeviceBlocked(
|
||||
this.props.userId, this.props.device.id, false
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var indicator = null, button = null;
|
||||
if (this.props.device.verified) {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_verified">✔</div>
|
||||
var indicator = null, blockButton = null, verifyButton = null;
|
||||
if (this.props.device.blocked) {
|
||||
blockButton = (
|
||||
<div className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unblock"
|
||||
onClick={this.onUnblockClick}>
|
||||
Unblock
|
||||
</div>
|
||||
);
|
||||
button = (
|
||||
} else {
|
||||
blockButton = (
|
||||
<div className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_block"
|
||||
onClick={this.onBlockClick}>
|
||||
Block
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.device.verified) {
|
||||
verifyButton = (
|
||||
<div className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unverify"
|
||||
onClick={this.onUnverifyClick}>
|
||||
Unverify
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
button = (
|
||||
verifyButton = (
|
||||
<div className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_verify"
|
||||
onClick={this.onVerifyClick}>
|
||||
Verify
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.device.blocked) {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_blocked">✖</div>
|
||||
);
|
||||
} else if (this.props.device.verified) {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_verified">✔</div>
|
||||
);
|
||||
|
||||
} else {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_unverified">?</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_MemberDeviceInfo">
|
||||
<div className="mx_MemberDeviceInfo_deviceId">{this.props.device.id}</div>
|
||||
<div className="mx_MemberDeviceInfo_deviceKey">{this.props.device.key}</div>
|
||||
{indicator}
|
||||
{button}
|
||||
{verifyButton}
|
||||
{blockButton}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -70,7 +70,7 @@ module.exports = React.createClass({
|
|||
|
||||
componentDidMount: function() {
|
||||
this._updateStateForNewMember(this.props.member);
|
||||
MatrixClientPeg.get().on("deviceVerified", this.onDeviceVerified);
|
||||
MatrixClientPeg.get().on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
|
@ -82,14 +82,14 @@ module.exports = React.createClass({
|
|||
componentWillUnmount: function() {
|
||||
var client = MatrixClientPeg.get();
|
||||
if (client) {
|
||||
client.removeListener("deviceVerified", this.onDeviceVerified);
|
||||
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
}
|
||||
if (this._cancelDeviceList) {
|
||||
this._cancelDeviceList();
|
||||
}
|
||||
},
|
||||
|
||||
onDeviceVerified: function(userId, device) {
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
if (userId == this.props.member.userId) {
|
||||
// no need to re-download the whole thing; just update our copy of
|
||||
// the list.
|
||||
|
@ -358,10 +358,15 @@ module.exports = React.createClass({
|
|||
];
|
||||
var existingRoomId;
|
||||
|
||||
var currentRoom = MatrixClientPeg.get().getRoom(this.props.member.roomId);
|
||||
var currentMembers = currentRoom.getJoinedMembers();
|
||||
// roomId can be null here because of a hack in MatrixChat.onUserClick where we
|
||||
// abuse this to view users rather than room members.
|
||||
var currentMembers;
|
||||
if (this.props.member.roomId) {
|
||||
var currentRoom = MatrixClientPeg.get().getRoom(this.props.member.roomId);
|
||||
currentMembers = currentRoom.getJoinedMembers();
|
||||
}
|
||||
// if we're currently in a 1:1 with this user, start a new chat
|
||||
if (currentMembers.length === 2 &&
|
||||
if (currentMembers && currentMembers.length === 2 &&
|
||||
userIds.indexOf(currentMembers[0].userId) !== -1 &&
|
||||
userIds.indexOf(currentMembers[1].userId) !== -1)
|
||||
{
|
||||
|
@ -535,7 +540,9 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<div>
|
||||
<h3>Devices</h3>
|
||||
{devComponents}
|
||||
<div className="mx_MemberInfo_devices">
|
||||
{devComponents}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -425,27 +425,7 @@ module.exports = React.createClass({
|
|||
|
||||
// For now, let's just order things by timestamp. It's really annoying
|
||||
// that a user disappears from sight just because they temporarily go offline
|
||||
/*
|
||||
var presenceMap = {
|
||||
online: 3,
|
||||
unavailable: 2,
|
||||
offline: 1
|
||||
};
|
||||
|
||||
var presenceOrdA = userA ? presenceMap[userA.presence] : 0;
|
||||
var presenceOrdB = userB ? presenceMap[userB.presence] : 0;
|
||||
|
||||
if (presenceOrdA != presenceOrdB) {
|
||||
return presenceOrdB - presenceOrdA;
|
||||
}
|
||||
*/
|
||||
|
||||
var lastActiveTsA = userA && userA.lastActiveTs ? userA.lastActiveTs : 0;
|
||||
var lastActiveTsB = userB && userB.lastActiveTs ? userB.lastActiveTs : 0;
|
||||
|
||||
// console.log("comparing ts: " + lastActiveTsA + " and " + lastActiveTsB);
|
||||
|
||||
return lastActiveTsB - lastActiveTsA;
|
||||
return userB.getLastActiveTs() - userA.getLastActiveTs();
|
||||
},
|
||||
|
||||
onSearchQueryChanged: function(input) {
|
||||
|
|
|
@ -33,16 +33,24 @@ module.exports = React.createClass({
|
|||
|
||||
// If invited by 3rd party invite, the email address the invite was sent to
|
||||
invitedEmail: React.PropTypes.string,
|
||||
canJoin: React.PropTypes.bool,
|
||||
|
||||
// A standard client/server API error object. If supplied, indicates that the
|
||||
// caller was unable to fetch details about the room for the given reason.
|
||||
error: React.PropTypes.object,
|
||||
|
||||
canPreview: React.PropTypes.bool,
|
||||
spinner: React.PropTypes.bool,
|
||||
room: React.PropTypes.object,
|
||||
|
||||
// The alias that was used to access this room, if appropriate
|
||||
// If given, this will be how the room is referred to (eg.
|
||||
// in error messages).
|
||||
roomAlias: React.PropTypes.object,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onJoinClick: function() {},
|
||||
canJoin: false,
|
||||
canPreview: true,
|
||||
};
|
||||
},
|
||||
|
@ -115,8 +123,24 @@ module.exports = React.createClass({
|
|||
);
|
||||
|
||||
}
|
||||
else if (this.props.canJoin) {
|
||||
var name = this.props.room ? this.props.room.name : "";
|
||||
else if (this.props.error) {
|
||||
var name = this.props.roomAlias || "This room";
|
||||
var error;
|
||||
if (this.props.error.errcode == 'M_NOT_FOUND') {
|
||||
error = name + " does not exist";
|
||||
} else {
|
||||
error = name + " is not accessible at this time";
|
||||
}
|
||||
joinBlock = (
|
||||
<div>
|
||||
<div className="mx_RoomPreviewBar_join_text">
|
||||
{ error }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
var name = this.props.room ? this.props.room.name : (this.props.room_alias || "");
|
||||
name = name ? <b>{ name }</b> : "a room";
|
||||
joinBlock = (
|
||||
<div>
|
||||
|
|
|
@ -21,6 +21,14 @@ var sdk = require('../../../index');
|
|||
var Modal = require('../../../Modal');
|
||||
var ObjectUtils = require("../../../ObjectUtils");
|
||||
var dis = require("../../../dispatcher");
|
||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
||||
|
||||
// parse a string as an integer; if the input is undefined, or cannot be parsed
|
||||
// as an integer, return a default.
|
||||
function parseIntWithDefault(val, def) {
|
||||
var res = parseInt(val);
|
||||
return isNaN(res) ? def : res;
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomSettings',
|
||||
|
@ -57,7 +65,7 @@ module.exports = React.createClass({
|
|||
tags_changed: false,
|
||||
tags: tags,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
isRoomPublished: this._originalIsRoomPublished, // loaded async in componentWillMount
|
||||
isRoomPublished: false, // loaded async in componentWillMount
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -199,11 +207,14 @@ module.exports = React.createClass({
|
|||
}
|
||||
});
|
||||
}
|
||||
console.log("Performing %s operations", promises.length);
|
||||
|
||||
// color scheme
|
||||
promises.push(this.saveColor());
|
||||
|
||||
// encryption
|
||||
promises.push(this.saveEncryption());
|
||||
|
||||
console.log("Performing %s operations", promises.length);
|
||||
return q.allSettled(promises);
|
||||
},
|
||||
|
||||
|
@ -217,6 +228,19 @@ module.exports = React.createClass({
|
|||
return this.refs.color_settings.saveSettings();
|
||||
},
|
||||
|
||||
saveEncryption: function () {
|
||||
if (!this.refs.encrypt) { return q(); }
|
||||
|
||||
var encrypt = this.refs.encrypt.checked;
|
||||
if (!encrypt) { return q(); }
|
||||
|
||||
var roomId = this.props.room.roomId;
|
||||
return MatrixClientPeg.get().sendStateEvent(
|
||||
roomId, "m.room.encryption",
|
||||
{ algorithm: "m.olm.v1.curve25519-aes-sha2" }
|
||||
);
|
||||
},
|
||||
|
||||
_hasDiff: function(strA, strB) {
|
||||
// treat undefined as an empty string because other components may blindly
|
||||
// call setName("") when there has been no diff made to the name!
|
||||
|
@ -251,7 +275,7 @@ module.exports = React.createClass({
|
|||
power_levels_changed: true
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
_yankValueFromEvent: function(stateEventType, keyName, defaultValue) {
|
||||
// E.g.("m.room.name","name") would yank the "name" content key from "m.room.name"
|
||||
var event = this.props.room.currentState.getStateEvents(stateEventType, '');
|
||||
|
@ -286,7 +310,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
_onRoomAccessRadioToggle: function(ev) {
|
||||
|
||||
// join_rule
|
||||
|
@ -359,6 +383,39 @@ module.exports = React.createClass({
|
|||
roomState.mayClientSendStateEvent("m.room.guest_access", cli))
|
||||
},
|
||||
|
||||
_renderEncryptionSection: function() {
|
||||
if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var cli = MatrixClientPeg.get();
|
||||
var roomState = this.props.room.currentState;
|
||||
var isEncrypted = cli.isRoomEncrypted(this.props.room.roomId);
|
||||
|
||||
var text = "Encryption is " + (isEncrypted ? "" : "not ") +
|
||||
"enabled in this room.";
|
||||
|
||||
var button;
|
||||
if (!isEncrypted &&
|
||||
roomState.mayClientSendStateEvent("m.room.encryption", cli)) {
|
||||
button = (
|
||||
<label>
|
||||
<input type="checkbox" ref="encrypt" />
|
||||
Enable encryption (warning: cannot be disabled again!)
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSettings_toggles">
|
||||
<h3>Encryption</h3>
|
||||
<label>{text}</label>
|
||||
{button}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
render: function() {
|
||||
// TODO: go through greying out things you don't have permission to change
|
||||
// (or turning them into informative stuff)
|
||||
|
@ -368,58 +425,29 @@ module.exports = React.createClass({
|
|||
var EditableText = sdk.getComponent('elements.EditableText');
|
||||
var PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||
|
||||
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
||||
var events_levels = (power_levels ? power_levels.getContent().events : {}) || {};
|
||||
var cli = MatrixClientPeg.get();
|
||||
var roomState = this.props.room.currentState;
|
||||
var user_id = cli.credentials.userId;
|
||||
|
||||
if (power_levels) {
|
||||
power_levels = power_levels.getContent();
|
||||
var power_level_event = roomState.getStateEvents('m.room.power_levels', '');
|
||||
var power_levels = power_level_event ? power_level_event.getContent() : {};
|
||||
var events_levels = power_levels.events || {};
|
||||
var user_levels = power_levels.users || {};
|
||||
|
||||
var ban_level = parseInt(power_levels.ban);
|
||||
var kick_level = parseInt(power_levels.kick);
|
||||
var redact_level = parseInt(power_levels.redact);
|
||||
var invite_level = parseInt(power_levels.invite || 0);
|
||||
var send_level = parseInt(power_levels.events_default || 0);
|
||||
var state_level = parseInt(power_levels.state_default || 50);
|
||||
var default_user_level = parseInt(power_levels.users_default || 0);
|
||||
var ban_level = parseIntWithDefault(power_levels.ban, 50);
|
||||
var kick_level = parseIntWithDefault(power_levels.kick, 50);
|
||||
var redact_level = parseIntWithDefault(power_levels.redact, 50);
|
||||
var invite_level = parseIntWithDefault(power_levels.invite, 50);
|
||||
var send_level = parseIntWithDefault(power_levels.events_default, 0);
|
||||
var state_level = power_level_event ? parseIntWithDefault(power_levels.state_default, 50) : 0;
|
||||
var default_user_level = parseIntWithDefault(power_levels.users_default, 0);
|
||||
|
||||
if (power_levels.ban == undefined) ban_level = 50;
|
||||
if (power_levels.kick == undefined) kick_level = 50;
|
||||
if (power_levels.redact == undefined) redact_level = 50;
|
||||
|
||||
var user_levels = power_levels.users || {};
|
||||
|
||||
var current_user_level = user_levels[user_id];
|
||||
if (current_user_level == undefined) current_user_level = default_user_level;
|
||||
|
||||
var power_level_level = events_levels["m.room.power_levels"];
|
||||
if (power_level_level == undefined) {
|
||||
power_level_level = state_level;
|
||||
}
|
||||
|
||||
var can_change_levels = current_user_level >= power_level_level;
|
||||
} else {
|
||||
var ban_level = 50;
|
||||
var kick_level = 50;
|
||||
var redact_level = 50;
|
||||
var invite_level = 0;
|
||||
var send_level = 0;
|
||||
var state_level = 0;
|
||||
var default_user_level = 0;
|
||||
|
||||
var user_levels = [];
|
||||
var events_levels = [];
|
||||
|
||||
var current_user_level = 0;
|
||||
|
||||
var power_level_level = 0;
|
||||
|
||||
var can_change_levels = false;
|
||||
var current_user_level = user_levels[user_id];
|
||||
if (current_user_level === undefined) {
|
||||
current_user_level = default_user_level;
|
||||
}
|
||||
|
||||
var state_default = (parseInt(power_levels ? power_levels.state_default : 0) || 0);
|
||||
var can_change_levels = roomState.mayClientSendStateEvent("m.room.power_levels", cli);
|
||||
|
||||
var canSetTag = !cli.isGuest();
|
||||
|
||||
|
@ -488,7 +516,7 @@ module.exports = React.createClass({
|
|||
|
||||
var tagsSection = null;
|
||||
if (canSetTag || self.state.tags) {
|
||||
var tagsSection =
|
||||
var tagsSection =
|
||||
<div className="mx_RoomSettings_tags">
|
||||
Tagged as: { canSetTag ?
|
||||
(tags.map(function(tag, i) {
|
||||
|
@ -609,10 +637,6 @@ module.exports = React.createClass({
|
|||
Members only (since they joined)
|
||||
</label>
|
||||
</div>
|
||||
<label className="mx_RoomSettings_encrypt">
|
||||
<input type="checkbox" />
|
||||
Encrypt room
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -677,6 +701,8 @@ module.exports = React.createClass({
|
|||
|
||||
{ bannedUsersSection }
|
||||
|
||||
{ this._renderEncryptionSection() }
|
||||
|
||||
<h3>Advanced</h3>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
This room's internal ID is <code>{ this.props.room.roomId }</code>
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket 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.
|
||||
*/
|
||||
|
||||
function enableEncyption(client, roomId, members) {
|
||||
members = members.slice(0);
|
||||
members.push(client.credentials.userId);
|
||||
// TODO: Check the keys actually match what keys the user has.
|
||||
// TODO: Don't redownload keys each time.
|
||||
return client.downloadKeys(members, "forceDownload").then(function(res) {
|
||||
return client.setRoomEncryption(roomId, {
|
||||
algorithm: "m.olm.v1.curve25519-aes-sha2",
|
||||
members: members,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function disableEncryption(client, roomId) {
|
||||
return client.disableRoomEncryption(roomId);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
enableEncryption: enableEncyption,
|
||||
disableEncryption: disableEncryption,
|
||||
}
|
|
@ -210,7 +210,7 @@ describe('TimelinePanel', function() {
|
|||
var N_EVENTS = 600;
|
||||
|
||||
// sadly, loading all those events takes a while
|
||||
this.timeout(N_EVENTS * 20);
|
||||
this.timeout(N_EVENTS * 40);
|
||||
|
||||
// client.getRoom is called a /lot/ in this test, so replace
|
||||
// sinon's spy with a fast noop.
|
||||
|
@ -220,12 +220,14 @@ describe('TimelinePanel', function() {
|
|||
for (var i = 0; i < N_EVENTS; i++) {
|
||||
timeline.addEvent(mkMessage());
|
||||
}
|
||||
console.log("added events to timeline");
|
||||
|
||||
var scrollDefer;
|
||||
var panel = ReactDOM.render(
|
||||
<TimelinePanel room={room} onScroll={()=>{scrollDefer.resolve()}} />,
|
||||
parentDiv
|
||||
);
|
||||
console.log("TimelinePanel rendered");
|
||||
|
||||
var messagePanel = ReactTestUtils.findRenderedComponentWithType(
|
||||
panel, sdk.getComponent('structures.MessagePanel'));
|
||||
|
@ -246,6 +248,7 @@ describe('TimelinePanel', function() {
|
|||
// need to go further
|
||||
return backPaginate();
|
||||
}
|
||||
console.log("paginated to end.");
|
||||
|
||||
// hopefully, we got to the start of the timeline
|
||||
expect(messagePanel.props.backPaginating).toBe(false);
|
||||
|
@ -259,6 +262,7 @@ describe('TimelinePanel', function() {
|
|||
expect(messagePanel.props.suppressFirstDateSeparator).toBe(true);
|
||||
|
||||
// back-paginate until we hit the start
|
||||
console.log("back paginating...");
|
||||
return backPaginate();
|
||||
}).then(() => {
|
||||
expect(messagePanel.props.suppressFirstDateSeparator).toBe(false);
|
||||
|
|
Loading…
Reference in New Issue