From 91ee5f8a423c274156985042470ed255faa3a00d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 2 Dec 2015 14:59:37 +0000 Subject: [PATCH 001/171] Add extra arg isGuest to MatrixClientPeg functions. Add GuestAccess class GuestAccess serves to monitor which rooms are being tracked for guest purposes --- src/GuestAccess.js | 52 ++++++++++++++++++++++++++++++++++++++++++ src/MatrixClientPeg.js | 20 ++++++++++++---- 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/GuestAccess.js diff --git a/src/GuestAccess.js b/src/GuestAccess.js new file mode 100644 index 0000000000..a76c2f2169 --- /dev/null +++ b/src/GuestAccess.js @@ -0,0 +1,52 @@ +/* +Copyright 2015 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. +*/ +import {MatrixClientPeg} from "./MatrixClientPeg"; +const ROOM_ID_KEY = "matrix-guest-room-ids"; + +class GuestAccess { + + constructor(localStorage) { + var existingRoomIds; + try { + existingRoomIds = JSON.parse( + localStorage.getItem(ROOM_ID_KEY) // an array + ); + } + catch (e) {} // don't care + this.rooms = new Set(existingRoomIds); + this.localStorage = localStorage; + } + + addRoom(roomId) { + this.rooms.add(roomId); + } + + removeRoom(roomId) { + this.rooms.delete(roomId); + } + + getRooms() { + return this.rooms.entries(); + } + + register() { + // nuke the rooms being watched from previous guest accesses if any. + localStorage.setItem(ROOM_ID_KEY, "[]"); + return MatrixClientPeg.get().registerGuest(); + } +} + +module.exports = GuestAccess; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index d940b69f27..21fe934ca5 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -33,7 +33,7 @@ function deviceId() { return id; } -function createClient(hs_url, is_url, user_id, access_token) { +function createClient(hs_url, is_url, user_id, access_token, isGuest) { var opts = { baseUrl: hs_url, idBaseUrl: is_url, @@ -47,6 +47,9 @@ function createClient(hs_url, is_url, user_id, access_token) { } matrixClient = Matrix.createClient(opts); + if (isGuest) { + matrixClient.setGuest(true); + } } if (localStorage) { @@ -54,8 +57,16 @@ if (localStorage) { var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; var access_token = localStorage.getItem("mx_access_token"); var user_id = localStorage.getItem("mx_user_id"); + var isGuest = localStorage.getItem("mx_is_guest") || false; + if (isGuest) { + try { + isGuest = JSON.parse(isGuest); + } + catch (e) {} // ignore + isGuest = Boolean(isGuest); // in case of null, make it false. + } if (access_token && user_id && hs_url) { - createClient(hs_url, is_url, user_id, access_token); + createClient(hs_url, is_url, user_id, access_token, isGuest); } } @@ -97,7 +108,7 @@ class MatrixClient { } } - replaceUsingAccessToken(hs_url, is_url, user_id, access_token) { + replaceUsingAccessToken(hs_url, is_url, user_id, access_token, isGuest) { if (localStorage) { try { localStorage.clear(); @@ -105,13 +116,14 @@ class MatrixClient { console.warn("Error using local storage"); } } - createClient(hs_url, is_url, user_id, access_token); + createClient(hs_url, is_url, user_id, access_token, isGuest); if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); localStorage.setItem("mx_is_url", is_url); localStorage.setItem("mx_user_id", user_id); localStorage.setItem("mx_access_token", access_token); + localStorage.setItem("mx_is_guest", isGuest); } catch (e) { console.warn("Error using local storage: can't persist session!"); } From d52b3149d4f074d9e66fd0f1b6268410bcc67250 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 10:37:53 +0000 Subject: [PATCH 002/171] Wire GuestAccess to MatrixClientPeg --- src/GuestAccess.js | 28 ++++++++++++++++++++++++---- src/MatrixClientPeg.js | 31 ++++++++++++++++--------------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/GuestAccess.js b/src/GuestAccess.js index a76c2f2169..b1a059425d 100644 --- a/src/GuestAccess.js +++ b/src/GuestAccess.js @@ -15,12 +15,14 @@ limitations under the License. */ import {MatrixClientPeg} from "./MatrixClientPeg"; const ROOM_ID_KEY = "matrix-guest-room-ids"; +const IS_GUEST_KEY = "matrix-is-guest"; class GuestAccess { constructor(localStorage) { var existingRoomIds; try { + this._isGuest = localStorage.getItem(IS_GUEST_KEY) === "true"; existingRoomIds = JSON.parse( localStorage.getItem(ROOM_ID_KEY) // an array ); @@ -32,20 +34,38 @@ class GuestAccess { addRoom(roomId) { this.rooms.add(roomId); + this._saveAndSetRooms(); } removeRoom(roomId) { this.rooms.delete(roomId); + this._saveAndSetRooms(); } getRooms() { return this.rooms.entries(); } - register() { - // nuke the rooms being watched from previous guest accesses if any. - localStorage.setItem(ROOM_ID_KEY, "[]"); - return MatrixClientPeg.get().registerGuest(); + isGuest() { + return this._isGuest; + } + + markAsGuest(isGuest) { + try { + this.localStorage.setItem(IS_GUEST_KEY, JSON.stringify(isGuest)); + // nuke the rooms being watched from previous guest accesses if any. + this.localStorage.setItem(ROOM_ID_KEY, "[]"); + } catch (e) {} // ignore. If they don't do LS, they'll just get a new account. + this._isGuest = isGuest; + this.rooms = new Set(); + } + + _saveAndSetRooms() { + let rooms = this.getRooms(); + MatrixClientPeg.get().setGuestRooms(rooms); + try { + this.localStorage.setItem(ROOM_ID_KEY, rooms); + } catch (e) {} } } diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 21fe934ca5..7a1ee781ab 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -18,6 +18,7 @@ limitations under the License. // A thing that holds your Matrix Client var Matrix = require("matrix-js-sdk"); +var GuestAccess = require("./GuestAccess"); var matrixClient = null; @@ -33,7 +34,7 @@ function deviceId() { return id; } -function createClient(hs_url, is_url, user_id, access_token, isGuest) { +function createClient(hs_url, is_url, user_id, access_token, guestAccess) { var opts = { baseUrl: hs_url, idBaseUrl: is_url, @@ -47,8 +48,10 @@ function createClient(hs_url, is_url, user_id, access_token, isGuest) { } matrixClient = Matrix.createClient(opts); - if (isGuest) { - matrixClient.setGuest(true); + if (guestAccess) { + console.log("Guest: %s", guestAccess.isGuest()); + matrixClient.setGuest(guestAccess.isGuest()); + matrixClient.setGuestRooms(guestAccess.getRooms()); } } @@ -57,20 +60,18 @@ if (localStorage) { var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; var access_token = localStorage.getItem("mx_access_token"); var user_id = localStorage.getItem("mx_user_id"); - var isGuest = localStorage.getItem("mx_is_guest") || false; - if (isGuest) { - try { - isGuest = JSON.parse(isGuest); - } - catch (e) {} // ignore - isGuest = Boolean(isGuest); // in case of null, make it false. - } + var guestAccess = new GuestAccess(localStorage); if (access_token && user_id && hs_url) { - createClient(hs_url, is_url, user_id, access_token, isGuest); + createClient(hs_url, is_url, user_id, access_token, guestAccess); } } class MatrixClient { + + constructor(guestAccess) { + this.guestAccess = guestAccess; + } + get() { return matrixClient; } @@ -116,14 +117,14 @@ class MatrixClient { console.warn("Error using local storage"); } } - createClient(hs_url, is_url, user_id, access_token, isGuest); + this.guestAccess.markAsGuest(isGuest); + createClient(hs_url, is_url, user_id, access_token, this.guestAccess); if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); localStorage.setItem("mx_is_url", is_url); localStorage.setItem("mx_user_id", user_id); localStorage.setItem("mx_access_token", access_token); - localStorage.setItem("mx_is_guest", isGuest); } catch (e) { console.warn("Error using local storage: can't persist session!"); } @@ -134,6 +135,6 @@ class MatrixClient { } if (!global.mxMatrixClient) { - global.mxMatrixClient = new MatrixClient(); + global.mxMatrixClient = new MatrixClient(new GuestAccess(localStorage)); } module.exports = global.mxMatrixClient; From d0ec84fe591734645a27bf5d0c687271a58a796b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 11:34:50 +0000 Subject: [PATCH 003/171] Hook up auto-registration as a guest to MatrixChat. --- src/MatrixClientPeg.js | 2 +- src/components/structures/MatrixChat.js | 52 +++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 7a1ee781ab..76fa102562 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -117,7 +117,7 @@ class MatrixClient { console.warn("Error using local storage"); } } - this.guestAccess.markAsGuest(isGuest); + this.guestAccess.markAsGuest(Boolean(isGuest)); createClient(hs_url, is_url, user_id, access_token, this.guestAccess); if (localStorage) { try { diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index a6d6716222..6bb16dafd2 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -40,7 +40,8 @@ module.exports = React.createClass({ config: React.PropTypes.object.isRequired, ConferenceHandler: React.PropTypes.any, onNewScreen: React.PropTypes.func, - registrationUrl: React.PropTypes.string + registrationUrl: React.PropTypes.string, + enableGuest: React.PropTypes.bool }, PageTypes: { @@ -75,15 +76,31 @@ module.exports = React.createClass({ }, componentDidMount: function() { + this._autoRegisterAsGuest = false; + if (this.props.enableGuest) { + if (!this.props.config || !this.props.config.default_hs_url) { + console.error("Cannot enable guest access: No supplied config prop for HS/IS URLs"); + } + else { + this._autoRegisterAsGuest = true; + } + } + this.dispatcherRef = dis.register(this.onAction); if (this.state.logged_in) { + // Don't auto-register as a guest. This applies if you refresh the page on a + // logged in client THEN hit the Sign Out button. + this._autoRegisterAsGuest = false; this.startMatrixClient(); } this.focusComposer = false; document.addEventListener("keydown", this.onKeyDown); window.addEventListener("focus", this.onFocus); + if (this.state.logged_in) { this.notifyNewScreen(''); + } else if (this._autoRegisterAsGuest) { + this._registerAsGuest(); } else { this.notifyNewScreen('login'); } @@ -115,6 +132,26 @@ module.exports = React.createClass({ } }, + _registerAsGuest: function() { + var self = this; + var config = this.props.config; + console.log("Doing guest login on %s", config.default_hs_url); + MatrixClientPeg.replaceUsingUrls( + config.default_hs_url, config.default_is_url + ); + MatrixClientPeg.get().registerGuest().done(function(creds) { + console.log("Registered as guest: %s", JSON.stringify(creds)); + }, function(err) { + console.error(err.data); + self._setAutoRegisterAsGuest(false); + }); + }, + + _setAutoRegisterAsGuest: function(shouldAutoRegister) { + this._autoRegisterAsGuest = shouldAutoRegister; + this.forceUpdate(); + }, + onAction: function(payload) { var roomIndexDelta = 1; @@ -130,6 +167,7 @@ module.exports = React.createClass({ MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.unset(); + this.notifyNewScreen('login'); this.replaceState({ logged_in: false, @@ -634,12 +672,20 @@ module.exports = React.createClass({ ); } - } else if (this.state.logged_in) { + } else if (this.state.logged_in || (!this.state.logged_in && this._autoRegisterAsGuest)) { var Spinner = sdk.getComponent('elements.Spinner'); + var logoutLink; + if (this.state.logged_in) { + logoutLink = ( + + Logout + + ); + } return (
- Logout + {logoutLink}
); } else if (this.state.screen == 'register') { From 92cf14d5e9dbf864b2619225de1140c54ee46b8e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 15:28:35 +0000 Subject: [PATCH 004/171] Support registration as a guest --- src/GuestAccess.js | 2 +- src/components/structures/MatrixChat.js | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/GuestAccess.js b/src/GuestAccess.js index b1a059425d..a76e198b4e 100644 --- a/src/GuestAccess.js +++ b/src/GuestAccess.js @@ -43,7 +43,7 @@ class GuestAccess { } getRooms() { - return this.rooms.entries(); + return Array.from(this.rooms.entries()); } isGuest() { diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 6bb16dafd2..4af6e96c96 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -140,7 +140,14 @@ module.exports = React.createClass({ config.default_hs_url, config.default_is_url ); MatrixClientPeg.get().registerGuest().done(function(creds) { - console.log("Registered as guest: %s", JSON.stringify(creds)); + console.log("Registered as guest: %s", creds.user_id); + self.onLoggedIn({ + userId: creds.user_id, + accessToken: creds.access_token, + homeserverUrl: config.default_hs_url, + identityServerUrl: config.default_is_url, + guest: true + }); }, function(err) { console.error(err.data); self._setAutoRegisterAsGuest(false); @@ -361,10 +368,11 @@ module.exports = React.createClass({ }, onLoggedIn: function(credentials) { - console.log("onLoggedIn => %s", credentials.userId); + credentials.guest = Boolean(credentials.guest); + console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest); MatrixClientPeg.replaceUsingAccessToken( credentials.homeserverUrl, credentials.identityServerUrl, - credentials.userId, credentials.accessToken + credentials.userId, credentials.accessToken, credentials.guest ); this.setState({ screen: undefined, From 4f915d622e77d3732e0d6a7b5e97748d5213406a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 3 Jan 2016 22:30:52 +0000 Subject: [PATCH 005/171] switch SVGs from imgs to objects --- src/components/structures/RoomView.js | 22 +++++++++++-------- src/components/views/rooms/MessageComposer.js | 6 ++--- src/components/views/rooms/RoomHeader.js | 18 +++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 56dd41d929..f1d408edfd 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1174,7 +1174,7 @@ module.exports = React.createClass({ if (this.state.syncState === "ERROR") { statusBar = (
- /!\ + /!\
Connectivity to the server has been lost. @@ -1194,7 +1194,7 @@ module.exports = React.createClass({
- ->| + Auto-complete @@ -1204,7 +1204,7 @@ module.exports = React.createClass({ else if (this.state.hasUnsentMessages) { statusBar = (
- /!\ + /!\
Some of your messages have not been sent. @@ -1268,7 +1268,7 @@ module.exports = React.createClass({ if (this.state.draggingFile) { fileDropTarget =
- Drop File Here
+
Drop File Here ; @@ -1306,24 +1306,28 @@ module.exports = React.createClass({ if (call.type === "video") { zoomButton = (
- Fill screen + ); videoMuteButton =
- + {call.isLocalVideoMuted()
} voiceMuteButton =
- + {call.isMicrophoneMuted()
if (!statusBar) { statusBar =
- + Active call
; } @@ -1334,7 +1338,7 @@ module.exports = React.createClass({ { videoMuteButton } { zoomButton } { statusBar } - + } diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 5ac4a24908..e4bfcf9a41 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -474,11 +474,11 @@ module.exports = React.createClass({ else { callButton =
- Voice call + videoCallButton =
- Video call + } @@ -493,7 +493,7 @@ module.exports = React.createClass({ - cancel_button =
Cancel
- save_button =
Save Changes
+ + name = +
+ +
+ + topic_el = + + + save_button =
Save
+ cancel_button =
Cancel
} else { // - var searchStatus; // don't display the search count until the search completes and // gives us a valid (possibly zero) searchCount. @@ -123,7 +158,9 @@ module.exports = React.createClass({ - if (topic) topic_el =
{ topic.getContent().topic }
; + + var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); + if (topic) topic_el =
{ topic.getContent().topic }
; } var roomAvatar = null; @@ -149,6 +186,18 @@ module.exports = React.createClass({ ; } + var right_row; + if (!this.props.editing) { + right_row = +
+ { forget_button } + { leave_button } +
+ +
+
; + } + header =
@@ -160,15 +209,9 @@ module.exports = React.createClass({ { topic_el }
- {cancel_button} {save_button} -
- { forget_button } - { leave_button } -
- -
-
+ {cancel_button} + {right_row} } diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 0864dc15c7..c601858757 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -136,9 +136,6 @@ module.exports = React.createClass({ render: function() { var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); - var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - if (topic) topic = topic.getContent().topic; - var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', ''); if (join_rule) join_rule = join_rule.getContent().join_rule; @@ -216,7 +213,7 @@ module.exports = React.createClass({ ./ } - var boundClick = self.onColorSchemeChanged.bind(this, i) + var boundClick = self.onColorSchemeChanged.bind(self, i) return (
-
+ var placeholderName = this.state.initialName ? "Unnamed Room" : this.props.room.name; + name =
From 6351258b0e6d5677f2a84460af0d6533bf401bb4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 10 Jan 2016 20:01:30 +0000 Subject: [PATCH 042/171] use room.getImplicitRoomName() from matthew/roomsettings2 branch of matrix-js-sdk for the placeholder roomname --- src/components/views/rooms/RoomHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 77ab01afb1..719695bc36 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -48,7 +48,7 @@ module.exports = React.createClass({ this.setState({ name: name ? name.getContent().name : '', - initialName: name ? name.getContent().name : '', + implicitName: this.props.room.getImplicitRoomName(MatrixClientPeg.get().credentials.userId), topic: topic ? topic.getContent().topic : '', }); } @@ -121,7 +121,7 @@ module.exports = React.createClass({ //
// if (topic) topic_el =
- var placeholderName = this.state.initialName ? "Unnamed Room" : this.props.room.name; + var placeholderName = this.state.implicitName || "Unnamed Room"; name =
From c30aeac31530e093adde022cdc658525a5021b21 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 11 Jan 2016 11:38:04 +0000 Subject: [PATCH 043/171] Set our own booleans instead of using isMounted --- src/components/structures/RoomView.js | 12 +++++++++--- src/components/structures/ScrollPanel.js | 12 ++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c91be22a47..32deec8a9b 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -100,6 +100,12 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { + // set a boolean to say we've been unmounted, which any pending + // promises can use to throw away their results. + // + // (We could use isMounted, but facebook have deprecated that.) + this.unmounted = true; + if (this.refs.messagePanel) { // disconnect the D&D event listeners from the message panel. This // is really just for hygiene - the messagePanel is going to be @@ -196,7 +202,7 @@ module.exports = React.createClass({ },*/ onRoomTimeline: function(ev, room, toStartOfTimeline) { - if (!this.isMounted()) return; + if (this.unmounted) return; // ignore anything that comes in whilst paginating: we get one // event for each new matrix event so this would cause a huge @@ -355,7 +361,7 @@ module.exports = React.createClass({ // we might have switched rooms since the paginate started - just bin // the results if so. - if (!this.isMounted()) return; + if (this.unmounted) return; this.setState({ room: MatrixClientPeg.get().getRoom(this.props.roomId), @@ -538,7 +544,7 @@ module.exports = React.createClass({ return searchPromise.then(function(results) { debuglog("search complete"); - if (!this.isMounted() || !self.state.searching || self.searchId != localSearchId) { + if (self.unmounted || !self.state.searching || self.searchId != localSearchId) { console.error("Discarding stale search results"); return; } diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index fc628b5a5e..8d26b2e365 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -112,6 +112,14 @@ module.exports = React.createClass({ this.checkFillState(); }, + componentWillUnmount: function() { + // set a boolean to say we've been unmounted, which any pending + // promises can use to throw away their results. + // + // (We could use isMounted(), but facebook have deprecated that.) + this.unmounted = true; + }, + onScroll: function(ev) { var sn = this._getScrollNode(); debuglog("Scroll event: offset now:", sn.scrollTop, "recentEventScroll:", this.recentEventScroll); @@ -158,7 +166,7 @@ module.exports = React.createClass({ // check the scroll state and send out backfill requests if necessary. checkFillState: function() { - if (!this.isMounted()) { + if (this.unmounted) { return; } @@ -350,7 +358,7 @@ module.exports = React.createClass({ * message panel. */ _getScrollNode: function() { - if (!this.isMounted()) { + if (this.unmounted) { // this shouldn't happen, but when it does, turn the NPE into // something more meaningful. throw new Error("ScrollPanel._getScrollNode called when unmounted"); From ddd8838b24f734514f825307d2898912284a2384 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 11 Jan 2016 12:46:12 +0000 Subject: [PATCH 044/171] linkify topics --- src/components/views/rooms/RoomHeader.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 719695bc36..bf0cc103f8 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -21,6 +21,12 @@ var sdk = require('../../../index'); var dis = require("../../../dispatcher"); var MatrixClientPeg = require('../../../MatrixClientPeg'); +var linkify = require('linkifyjs'); +var linkifyElement = require('linkifyjs/element'); +var linkifyMatrix = require('../../../linkify-matrix'); + +linkifyMatrix(linkify); + module.exports = React.createClass({ displayName: 'RoomHeader', @@ -54,6 +60,12 @@ module.exports = React.createClass({ } }, + componentDidUpdate: function() { + if (this.refs.topic) { + linkifyElement(this.refs.topic, linkifyMatrix.options); + } + }, + onVideoClick: function(e) { dis.dispatch({ action: 'place_call', @@ -121,7 +133,10 @@ module.exports = React.createClass({ //
// if (topic) topic_el =
- var placeholderName = this.state.implicitName || "Unnamed Room"; + var placeholderName = "Unnamed Room"; + if (this.state.implicitName && this.state.implicitName !== '?') { + placeholderName += " (" + this.state.implicitName + ")"; + } name =
@@ -158,13 +173,13 @@ module.exports = React.createClass({
{ this.props.room.name }
{ searchStatus } -
+
var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - if (topic) topic_el =
{ topic.getContent().topic }
; + if (topic) topic_el =
{ topic.getContent().topic }
; } var roomAvatar = null; @@ -204,7 +219,7 @@ module.exports = React.createClass({ header =
-
+
{ roomAvatar }
From fcdfce3760076787a57e6032417f30d0f98785a8 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 11 Jan 2016 15:12:55 +0000 Subject: [PATCH 045/171] Toggle auto-reg off when successfully registered as a guest. Otherwise if you refresh -> auto-reg -> logout, you will get an infinite spinner as it will think it is auto-registering again. --- src/components/structures/MatrixChat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 118b43ef9a..320dad09b3 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -152,6 +152,7 @@ module.exports = React.createClass({ ); MatrixClientPeg.get().registerGuest().done(function(creds) { console.log("Registered as guest: %s", creds.user_id); + self._setAutoRegisterAsGuest(false); self.onLoggedIn({ userId: creds.user_id, accessToken: creds.access_token, @@ -185,7 +186,6 @@ module.exports = React.createClass({ MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.unset(); - this.notifyNewScreen('login'); this.replaceState({ logged_in: false, From 243b2e4587c5b82255be14168d7cedf32574eea8 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 11 Jan 2016 15:28:59 +0000 Subject: [PATCH 046/171] Make the scrollpanel update itself correctly on video resize. When we first get video, the video component will resize itself. This will cause the page to be reflowed, but that doesn't trigger an update on the gemini scrollbar. We therefore need to force an update on the messagepanel. Implement this by providing an onResize property on the CallView component. We need to do the same when we change the maxHeight on the video panel. The same applies to resizing of the MessageComposer. That was previously handled with a fugly roomView.forceUpdate() from MessageComposer - make it use the same mechanism. Finally: the messageComposer is at least 70 pixels, and up to 100 pixels high - not 36. Fix the auxPanelMaxHeight calculation - and use a static constant rather than hardcoding the number to avoid this happening again. --- src/components/structures/RoomView.js | 26 +++++++++++++++---- src/components/views/rooms/MessageComposer.js | 23 +++++++++++----- src/components/views/voip/CallView.js | 8 +++++- src/components/views/voip/VideoFeed.js | 22 +++++++++++++++- src/components/views/voip/VideoView.js | 2 +- 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 26c2c6a1bc..c622a7d769 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1064,17 +1064,23 @@ module.exports = React.createClass({ // a maxHeight on the underlying remote video tag. var auxPanelMaxHeight; if (this.refs.callView) { - // XXX: don't understand why we have to call findDOMNode here in react 0.14 - it should already be a DOM node. - var video = ReactDOM.findDOMNode(this.refs.callView.refs.video.refs.remote); + var video = this.refs.callView.getVideoView().getRemoteVideoElement(); // header + footer + status + give us at least 100px of scrollback at all times. - auxPanelMaxHeight = window.innerHeight - (83 + 72 + 36 + 100); + auxPanelMaxHeight = window.innerHeight - + (83 + 72 + + sdk.getComponent('rooms.MessageComposer').MAX_HEIGHT + + 100); // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway // but it's better than the video going missing entirely if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; video.style.maxHeight = auxPanelMaxHeight + "px"; + + // the above might have made the video panel resize itself, so now + // we need to tell the gemini panel to adapt. + this.onChildResize(); } }, @@ -1109,6 +1115,15 @@ module.exports = React.createClass({ }); }, + onChildResize: function() { + // When the video or the message composer resizes, the scroll panel + // also changes size. Work around GeminiScrollBar fail by telling it + // about it. This also ensures that the scroll offset is updated. + if (this.refs.messagePanel) { + this.refs.messagePanel.forceUpdate(); + } + }, + render: function() { var RoomHeader = sdk.getComponent('rooms.RoomHeader'); var MessageComposer = sdk.getComponent('rooms.MessageComposer'); @@ -1299,7 +1314,7 @@ module.exports = React.createClass({ if (canSpeak) { messageComposer = } @@ -1402,7 +1417,8 @@ module.exports = React.createClass({ } /> { fileDropTarget }
- + { conferenceCallNotification } { aux }
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 083cee46a2..f2894bd6b3 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -65,8 +65,17 @@ function mdownToHtml(mdown) { module.exports = React.createClass({ displayName: 'MessageComposer', + statics: { + // the height we limit the composer to + MAX_HEIGHT: 100, + }, + propTypes: { - tabComplete: React.PropTypes.any + tabComplete: React.PropTypes.any, + + // a callback which is called when the height of the composer is + // changed due to a change in content. + onResize: React.PropTypes.function, }, componentWillMount: function() { @@ -237,13 +246,15 @@ module.exports = React.createClass({ // scrollHeight is at least equal to clientHeight, so we have to // temporarily crimp clientHeight to 0 to get an accurate scrollHeight value this.refs.textarea.style.height = "0px"; - var newHeight = this.refs.textarea.scrollHeight < 100 ? this.refs.textarea.scrollHeight : 100; + var newHeight = Math.min(this.refs.textarea.scrollHeight, + this.constructor.MAX_HEIGHT); this.refs.textarea.style.height = Math.ceil(newHeight) + "px"; - if (this.props.roomView) { - // kick gemini-scrollbar to re-layout - this.props.roomView.forceUpdate(); - } this.oldScrollHeight = this.refs.textarea.scrollHeight; + + if (this.props.onResize) { + // kick gemini-scrollbar to re-layout + this.props.onResize(); + } }, onKeyUp: function(ev) { diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js index bc82556163..98bad37467 100644 --- a/src/components/views/voip/CallView.js +++ b/src/components/views/voip/CallView.js @@ -33,6 +33,12 @@ var MatrixClientPeg = require("../../../MatrixClientPeg"); module.exports = React.createClass({ displayName: 'CallView', + propTypes: { + // a callback which is called when the video within the callview + // due to a change in video metadata + onResize: React.PropTypes.function, + }, + componentDidMount: function() { this.dispatcherRef = dis.register(this.onAction); if (this.props.room) { @@ -97,7 +103,7 @@ module.exports = React.createClass({ render: function(){ var VideoView = sdk.getComponent('voip.VideoView'); return ( - + ); } }); diff --git a/src/components/views/voip/VideoFeed.js b/src/components/views/voip/VideoFeed.js index e4833dba9f..88d1b50cce 100644 --- a/src/components/views/voip/VideoFeed.js +++ b/src/components/views/voip/VideoFeed.js @@ -21,9 +21,29 @@ var React = require('react'); module.exports = React.createClass({ displayName: 'VideoFeed', + propTypes: { + // a callback which is called when the video element is resized + // due to a change in video metadata + onResize: React.PropTypes.function, + }, + + componentDidMount() { + this.refs.vid.addEventListener('resize', this.onResize); + }, + + componentWillUnmount() { + this.refs.vid.removeEventListener('resize', this.onResize); + }, + + onResize: function(e) { + if(this.props.onResize) { + this.props.onResize(e); + } + }, + render: function() { return ( -