From 6f646260aab9b498b3feb24a2b1b52c539a33b7c Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 Nov 2015 17:31:32 +0000 Subject: [PATCH 01/16] WIP of component indexing update --- package.json | 1 + src/component-index.js | 62 ++++++++++++++++++++++ src/skins/vector/skindex.js | 100 ----------------------------------- src/skins/vector/skinfo.json | 3 -- src/vector/index.js | 2 +- 5 files changed, 64 insertions(+), 104 deletions(-) create mode 100644 src/component-index.js delete mode 100644 src/skins/vector/skindex.js delete mode 100644 src/skins/vector/skinfo.json diff --git a/package.json b/package.json index caed363d97..e41a67b739 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "license": "Apache-2.0", "style": "bundle.css", + "matrix-react-parent": "matrix-react-sdk", "scripts": { "reskindex": "reskindex vector -h src/skins/vector/header", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", diff --git a/src/component-index.js b/src/component-index.js new file mode 100644 index 0000000000..0fc5b272d0 --- /dev/null +++ b/src/component-index.js @@ -0,0 +1,62 @@ +/* +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. +*/ + +/* + * THIS FILE IS AUTO-GENERATED + * You can edit it you like, but your changes will be overwritten, + * so you'd just be trying to swim upstream like a salmon. + * You are not a salmon. + */ + +module.exports.components = require('matrix-react-sdk/lib/component-index').components; + +module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); +module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); +module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); +module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView'); +module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner'); +module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm'); +module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig'); +module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp'); +module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView'); + +// Old, unported Vector views: +module.exports.components['molecules.BottomLeftMenu'] = require('./skins/vector/views/molecules/BottomLeftMenu'); +module.exports.components['molecules.BottomLeftMenuTile'] = require('./skins/vector/views/molecules/BottomLeftMenuTile'); +module.exports.components['molecules.DateSeparator'] = require('./skins/vector/views/molecules/DateSeparator'); +module.exports.components['molecules.MatrixToolbar'] = require('./skins/vector/views/molecules/MatrixToolbar'); +module.exports.components['molecules.MessageContextMenu'] = require('./skins/vector/views/molecules/MessageContextMenu'); +module.exports.components['molecules.RoomCreate'] = require('./skins/vector/views/molecules/RoomCreate'); +module.exports.components['molecules.RoomDropTarget'] = require('./skins/vector/views/molecules/RoomDropTarget'); +module.exports.components['molecules.RoomTooltip'] = require('./skins/vector/views/molecules/RoomTooltip'); +module.exports.components['molecules.SearchBar'] = require('./skins/vector/views/molecules/SearchBar'); +module.exports.components['molecules.SenderProfile'] = require('./skins/vector/views/molecules/SenderProfile'); +module.exports.components['organisms.CreateRoom'] = require('./skins/vector/views/organisms/CreateRoom'); +module.exports.components['organisms.ErrorDialog'] = require('./skins/vector/views/organisms/ErrorDialog'); +module.exports.components['organisms.LeftPanel'] = require('./skins/vector/views/organisms/LeftPanel'); +module.exports.components['organisms.LogoutPrompt'] = require('./skins/vector/views/organisms/LogoutPrompt'); +module.exports.components['organisms.MemberList'] = require('./skins/vector/views/organisms/MemberList'); +module.exports.components['organisms.Notifier'] = require('./skins/vector/views/organisms/Notifier'); +module.exports.components['organisms.QuestionDialog'] = require('./skins/vector/views/organisms/QuestionDialog'); +module.exports.components['organisms.RightPanel'] = require('./skins/vector/views/organisms/RightPanel'); +module.exports.components['organisms.RoomDirectory'] = require('./skins/vector/views/organisms/RoomDirectory'); +module.exports.components['organisms.RoomList'] = require('./skins/vector/views/organisms/RoomList'); +module.exports.components['organisms.RoomSubList'] = require('./skins/vector/views/organisms/RoomSubList'); +module.exports.components['organisms.RoomView'] = require('./skins/vector/views/organisms/RoomView'); +module.exports.components['organisms.UserSettings'] = require('./skins/vector/views/organisms/UserSettings'); +module.exports.components['organisms.ViewSource'] = require('./skins/vector/views/organisms/ViewSource'); +module.exports.components['pages.CompatibilityPage'] = require('./skins/vector/views/pages/CompatibilityPage'); +module.exports.components['pages.MatrixChat'] = require('./skins/vector/views/pages/MatrixChat'); diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js deleted file mode 100644 index 1e747c62da..0000000000 --- a/src/skins/vector/skindex.js +++ /dev/null @@ -1,100 +0,0 @@ -/* -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. -*/ - -/* - * THIS FILE IS AUTO-GENERATED - * You can edit it you like, but your changes will be overwritten, - * so you'd just be trying to swim upstream like a salmon. - * You are not a salmon. - */ - -var skin = {}; - -// Vector-specific stuff -skin['elements.Spinner'] = require('../../components/views/elements/Spinner'); -skin['elements.ImageView'] = require('../../components/views/elements/ImageView'); -skin['messages.MessageTimestamp'] = require('../../components/views/messages/MessageTimestamp'); -skin['rooms.RoomTile'] = require('../../components/views/rooms/RoomDNDView'); - -// TODO: Fix this so matrix-react-sdk stuff is in react SDK skindex? -skin['avatars.RoomAvatar'] = require('matrix-react-sdk/lib/components/views/avatars/RoomAvatar'); -skin['avatars.MemberAvatar'] = require('matrix-react-sdk/lib/components/views/avatars/MemberAvatar'); - -skin['settings.EnableNotificationsButton'] = require('matrix-react-sdk/lib/components/views/settings/EnableNotificationsButton'); -skin['settings.ChangeAvatar'] = require('matrix-react-sdk/lib/components/views/settings/ChangeAvatar'); -skin['settings.ChangeDisplayName'] = require('matrix-react-sdk/lib/components/views/settings/ChangeDisplayName'); -skin['settings.ChangePassword'] = require('matrix-react-sdk/lib/components/views/settings/ChangePassword'); - -skin['elements.EditableText'] = require('matrix-react-sdk/lib/components/views/elements/EditableText'); -skin['elements.ProgressBar'] = require('matrix-react-sdk/lib/components/views/elements/ProgressBar'); -skin['elements.UserSelector'] = require('matrix-react-sdk/lib/components/views/elements/UserSelector'); - -skin['messages.MessageComposer'] = require('matrix-react-sdk/lib/components/views/messages/MessageComposer'); -skin['messages.TextualEvent'] = require('matrix-react-sdk/lib/components/views/messages/TextualEvent'); -skin['messages.MRoomMemberEvent'] = require('matrix-react-sdk/lib/components/views/messages/MRoomMemberEvent'); -skin['messages.Event'] = require('matrix-react-sdk/lib/components/views/messages/Event'); -skin['messages.Message'] = require('matrix-react-sdk/lib/components/views/messages/Message'); -skin['messages.MFileMessage'] = require('matrix-react-sdk/lib/components/views/messages/MFileMessage'); -skin['messages.MImageMessage'] = require('matrix-react-sdk/lib/components/views/messages/MImageMessage'); -skin['messages.MVideoMessage'] = require('matrix-react-sdk/lib/components/views/messages/MVideoMessage'); -skin['messages.TextualMessage'] = require('matrix-react-sdk/lib/components/views/messages/TextualMessage'); -skin['messages.UnknownMessage'] = require('matrix-react-sdk/lib/components/views/messages/UnknownMessage'); - -skin['rooms.MemberInfo'] = require('matrix-react-sdk/lib/components/views/rooms/MemberInfo'); -skin['rooms.RoomHeader'] = require('matrix-react-sdk/lib/components/views/rooms/RoomHeader'); -skin['rooms.RoomSettings'] = require('matrix-react-sdk/lib/components/views/rooms/RoomSettings'); -skin['rooms.MemberTile'] = require('matrix-react-sdk/lib/components/views/rooms/MemberTile'); - -skin['create_room.CreateRoomButton'] = require('matrix-react-sdk/lib/components/views/create_room/CreateRoomButton'); -skin['create_room.Presets'] = require('matrix-react-sdk/lib/components/views/create_room/Presets'); -skin['create_room.RoomAlias'] = require('matrix-react-sdk/lib/components/views/create_room/RoomAlias'); - -skin['voip.CallView'] = require('matrix-react-sdk/lib/components/views/voip/CallView'); -skin['voip.IncomingCallBox'] = require('matrix-react-sdk/lib/components/views/voip/IncomingCallBox'); -skin['voip.VideoView'] = require('matrix-react-sdk/lib/components/views/voip/VideoView'); -skin['voip.VideoFeed'] = require('matrix-react-sdk/lib/components/views/voip/VideoFeed'); - - - -// Old style stuff -skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu'); -skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile'); -skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator'); -skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar'); -skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu'); -skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate'); -skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget'); -skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip'); -skin['molecules.SearchBar'] = require('./views/molecules/SearchBar'); -skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile'); -skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom'); -skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog'); -skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel'); -skin['organisms.LogoutPrompt'] = require('./views/organisms/LogoutPrompt'); -skin['organisms.MemberList'] = require('./views/organisms/MemberList'); -skin['organisms.Notifier'] = require('./views/organisms/Notifier'); -skin['organisms.QuestionDialog'] = require('./views/organisms/QuestionDialog'); -skin['organisms.RightPanel'] = require('./views/organisms/RightPanel'); -skin['organisms.RoomDirectory'] = require('./views/organisms/RoomDirectory'); -skin['organisms.RoomList'] = require('./views/organisms/RoomList'); -skin['organisms.RoomSubList'] = require('./views/organisms/RoomSubList'); -skin['organisms.RoomView'] = require('./views/organisms/RoomView'); -skin['organisms.UserSettings'] = require('./views/organisms/UserSettings'); -skin['organisms.ViewSource'] = require('./views/organisms/ViewSource'); -skin['pages.CompatibilityPage'] = require('./views/pages/CompatibilityPage'); -skin['pages.MatrixChat'] = require('./views/pages/MatrixChat'); - -module.exports = skin; diff --git a/src/skins/vector/skinfo.json b/src/skins/vector/skinfo.json deleted file mode 100644 index 287ff9e237..0000000000 --- a/src/skins/vector/skinfo.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseSkin": "" -} diff --git a/src/vector/index.js b/src/vector/index.js index 87cbd0b661..49ee3b5fa9 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -20,7 +20,7 @@ var RunModernizrTests = require("./modernizr"); // this side-effects a global var React = require("react"); var ReactDOM = require("react-dom"); var sdk = require("matrix-react-sdk"); -sdk.loadSkin(require('../skins/vector/skindex')); +sdk.loadSkin(require('../component-index')); sdk.loadModule(require('../modules/VectorConferenceHandler')); var qs = require("querystring"); From 6a4038daeb562e58e8f62e1102fb8fa490d6c7a5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 Nov 2015 17:56:55 +0000 Subject: [PATCH 02/16] Add 'replaces' tag to mark that the RoomDNDView is a RoomTile --- src/components/views/rooms/RoomDNDView.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomDNDView.js b/src/components/views/rooms/RoomDNDView.js index f096723f7f..419aac746a 100644 --- a/src/components/views/rooms/RoomDNDView.js +++ b/src/components/views/rooms/RoomDNDView.js @@ -201,4 +201,6 @@ DragSource('RoomTile', roomTileSource, function(connect, monitor) { // You can ask the monitor about the current drag state: isDragging: monitor.isDragging() }; -})(RoomTile)); \ No newline at end of file +})(RoomTile)); + +module.exports.replaces = 'RoomTile'; From 021056cfd18a5defa9f254266db066168fdd4efe Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 Nov 2015 18:00:54 +0000 Subject: [PATCH 03/16] Move copyright header --- package.json | 2 +- src/{skins/vector => }/header | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{skins/vector => }/header (100%) diff --git a/package.json b/package.json index e41a67b739..0598201d5b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "style": "bundle.css", "matrix-react-parent": "matrix-react-sdk", "scripts": { - "reskindex": "reskindex vector -h src/skins/vector/header", + "reskindex": "reskindex -h src/header", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch", "build:compile": "babel --source-maps -d lib src", diff --git a/src/skins/vector/header b/src/header similarity index 100% rename from src/skins/vector/header rename to src/header From e304a1925d4e349214c6046b78983f0db8445ecb Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 1 Dec 2015 16:12:38 +0000 Subject: [PATCH 04/16] Add VectorLoginFooter with vector references --- .../views/login/VectorLoginFooter.js | 34 +++++++++++++++++++ src/skins/vector/skindex.js | 1 + 2 files changed, 35 insertions(+) create mode 100644 src/components/views/login/VectorLoginFooter.js diff --git a/src/components/views/login/VectorLoginFooter.js b/src/components/views/login/VectorLoginFooter.js new file mode 100644 index 0000000000..cf9841d6f8 --- /dev/null +++ b/src/components/views/login/VectorLoginFooter.js @@ -0,0 +1,34 @@ +/* +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. +*/ + +'use strict'; + +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'VectorLoginFooter', + + render: function() { + return ( +
+ blog  ·   + twitter  ·   + github  ·   + powered by Matrix +
+ ); + } +}); diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js index d5b559c68c..bbc5e54e6d 100644 --- a/src/skins/vector/skindex.js +++ b/src/skins/vector/skindex.js @@ -36,6 +36,7 @@ skin['rooms.RoomDropTarget'] = require('../../components/views/rooms/RoomDropTar skin['rooms.RoomTooltip'] = require('../../components/views/rooms/RoomTooltip'); skin['rooms.SearchBar'] = require('../../components/views/rooms/SearchBar'); skin['globals.MatrixToolbar'] = require('../../components/views/globals/MatrixToolbar'); +skin['login.LoginFooter'] = require('../../components/views/login/VectorLoginFooter'); skin['structures.BottomLeftMenu'] = require('../../components/structures/BottomLeftMenu'); skin['structures.LeftPanel'] = require('../../components/structures/LeftPanel'); skin['structures.RightPanel'] = require('../../components/structures/RightPanel'); From 8d31f72f8320294ec30a9210606ccd7619532b73 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 1 Dec 2015 16:27:23 +0000 Subject: [PATCH 05/16] Add VectorLoginHeader --- .../views/login/VectorLoginHeader.js | 31 +++++++++++++++++++ src/skins/vector/skindex.js | 1 + 2 files changed, 32 insertions(+) create mode 100644 src/components/views/login/VectorLoginHeader.js diff --git a/src/components/views/login/VectorLoginHeader.js b/src/components/views/login/VectorLoginHeader.js new file mode 100644 index 0000000000..2c1d9072a1 --- /dev/null +++ b/src/components/views/login/VectorLoginHeader.js @@ -0,0 +1,31 @@ +/* +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. +*/ + +'use strict'; + +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'VectorLoginHeader', + + render: function() { + return ( +
+ vector +
+ ); + } +}); diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js index bbc5e54e6d..f847bb39c4 100644 --- a/src/skins/vector/skindex.js +++ b/src/skins/vector/skindex.js @@ -37,6 +37,7 @@ skin['rooms.RoomTooltip'] = require('../../components/views/rooms/RoomTooltip'); skin['rooms.SearchBar'] = require('../../components/views/rooms/SearchBar'); skin['globals.MatrixToolbar'] = require('../../components/views/globals/MatrixToolbar'); skin['login.LoginFooter'] = require('../../components/views/login/VectorLoginFooter'); +skin['login.LoginHeader'] = require('../../components/views/login/VectorLoginHeader'); skin['structures.BottomLeftMenu'] = require('../../components/structures/BottomLeftMenu'); skin['structures.LeftPanel'] = require('../../components/structures/LeftPanel'); skin['structures.RightPanel'] = require('../../components/structures/RightPanel'); From a2b28b826cf6bb8c5cfc9b7cf700722b21ec8a8e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 1 Dec 2015 16:48:51 +0000 Subject: [PATCH 06/16] Add CustomServerDialog for vector --- .../views/login/VectorCustomServerDialog.js | 52 +++++++++++++++++++ src/skins/vector/skindex.js | 6 ++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/components/views/login/VectorCustomServerDialog.js diff --git a/src/components/views/login/VectorCustomServerDialog.js b/src/components/views/login/VectorCustomServerDialog.js new file mode 100644 index 0000000000..b40675b76b --- /dev/null +++ b/src/components/views/login/VectorCustomServerDialog.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. +*/ + +var React = require("react"); + +module.exports = React.createClass({ + displayName: 'VectorCustomServerDialog', + + render: function() { + console.log("State: %s", JSON.stringify(this.state)); + console.log("Props: %s", JSON.stringify(this.props)); + return ( +
+
+ Custom Server Options +
+
+ + You can use the custom server options to log into other Matrix + servers by specifying a different Home server URL. +
+ This allows you to use Vector with an existing Matrix account on + a different Home server. +
+
+ You can also set a custom Identity server but this will affect + people's ability to find you if you use a server in a group other + than the main Matrix.org group. +
+
+
+ +
+
+ ); + } +}); diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js index f847bb39c4..1b6f1b70c4 100644 --- a/src/skins/vector/skindex.js +++ b/src/skins/vector/skindex.js @@ -24,6 +24,10 @@ limitations under the License. var skin = {}; // Vector-specific stuff +skin['login.LoginFooter'] = require('../../components/views/login/VectorLoginFooter'); +skin['login.LoginHeader'] = require('../../components/views/login/VectorLoginHeader'); +skin['login.CustomServerDialog'] = require("../../components/views/login/VectorCustomServerDialog"); + skin['elements.Spinner'] = require('../../components/views/elements/Spinner'); skin['elements.ImageView'] = require('../../components/views/elements/ImageView'); skin['messages.MessageTimestamp'] = require('../../components/views/messages/MessageTimestamp'); @@ -36,8 +40,6 @@ skin['rooms.RoomDropTarget'] = require('../../components/views/rooms/RoomDropTar skin['rooms.RoomTooltip'] = require('../../components/views/rooms/RoomTooltip'); skin['rooms.SearchBar'] = require('../../components/views/rooms/SearchBar'); skin['globals.MatrixToolbar'] = require('../../components/views/globals/MatrixToolbar'); -skin['login.LoginFooter'] = require('../../components/views/login/VectorLoginFooter'); -skin['login.LoginHeader'] = require('../../components/views/login/VectorLoginHeader'); skin['structures.BottomLeftMenu'] = require('../../components/structures/BottomLeftMenu'); skin['structures.LeftPanel'] = require('../../components/structures/LeftPanel'); skin['structures.RightPanel'] = require('../../components/structures/RightPanel'); From e21d435d8489c8bea8e427d62dbae07d518bcff0 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 1 Dec 2015 16:57:40 +0000 Subject: [PATCH 07/16] Remove debug logging --- src/components/views/login/VectorCustomServerDialog.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/login/VectorCustomServerDialog.js b/src/components/views/login/VectorCustomServerDialog.js index b40675b76b..4fb18fffdb 100644 --- a/src/components/views/login/VectorCustomServerDialog.js +++ b/src/components/views/login/VectorCustomServerDialog.js @@ -20,8 +20,6 @@ module.exports = React.createClass({ displayName: 'VectorCustomServerDialog', render: function() { - console.log("State: %s", JSON.stringify(this.state)); - console.log("Props: %s", JSON.stringify(this.props)); return (
From 2c12b9128b5fe4c2dff8656f3e9c8bab09353971 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 1 Dec 2015 16:57:36 +0000 Subject: [PATCH 08/16] highlight binged rooms more clearly --- src/skins/vector/css/molecules/RoomTile.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/skins/vector/css/molecules/RoomTile.css b/src/skins/vector/css/molecules/RoomTile.css index 898670d6d4..ac790fd8c5 100644 --- a/src/skins/vector/css/molecules/RoomTile.css +++ b/src/skins/vector/css/molecules/RoomTile.css @@ -112,6 +112,10 @@ limitations under the License. color: #76cfa6 ! important; } +.mx_RoomTile_highlight .mx_RoomTile_name { + color: #ff0064 ! important; +} + .mx_RoomTile.mx_RoomTile_selected .mx_RoomTile_name { background: url('img/selected.png'); background-repeat: no-repeat; From 7ff5e42f3e52da454466c614f51d0804bbee0096 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 1 Dec 2015 18:05:43 +0000 Subject: [PATCH 09/16] Run the CSS through webpack so we can pull in CSS files from modules with require rather than symlinking into the node_module directory which is breaking people on different npm versions. --- .gitignore | 2 +- package.json | 11 +++++++---- src/skins/vector/css/gemini-scrollbar.css | 1 - src/skins/vector/css/gfm.css | 1 - src/skins/vector/css/github.css | 1 - src/vector/index.js | 7 +++++++ webpack.config.js | 6 ++++++ 7 files changed, 21 insertions(+), 8 deletions(-) delete mode 120000 src/skins/vector/css/gemini-scrollbar.css delete mode 120000 src/skins/vector/css/gfm.css delete mode 120000 src/skins/vector/css/github.css diff --git a/.gitignore b/.gitignore index 7cbd6fc236..f5a3b1bdcd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ lib .DS_Store key.pem cert.pem -build +vector/components.css diff --git a/package.json b/package.json index caed363d97..0e7f21b0e0 100644 --- a/package.json +++ b/package.json @@ -12,22 +12,25 @@ "scripts": { "reskindex": "reskindex vector -h src/skins/vector/header", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", - "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch", + "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch", "build:compile": "babel --source-maps -d lib src", "build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js", "build": "npm run build:css && npm run build:compile && npm run build:bundle", "start:js": "webpack -w src/vector/index.js vector/bundle.js", - "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css", + "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css", "//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270", "start": "parallelshell \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"", - "clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map", + "clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css*", "prepublish": "npm run build:css && npm run build:compile" }, "dependencies": { "classnames": "^2.1.2", + "extract-text-webpack-plugin": "^0.9.1", "filesize": "^3.1.2", "flux": "~2.0.3", + "gemini-scrollbar": "^1.3.0", "gfm.css": "^1.1.1", + "highlight.js": "^8.9.1", "linkifyjs": "^2.0.0-beta.4", "matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop", "matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop", @@ -44,12 +47,12 @@ "babel-core": "^5.8.25", "babel-loader": "^5.3.2", "catw": "^1.0.1", + "css-raw-loader": "^0.1.1", "http-server": "^0.8.4", "json-loader": "^0.5.3", "parallelshell": "^1.2.0", "rimraf": "^2.4.3", "source-map-loader": "^0.1.5", - "uglifycss": "0.0.15", "webpack": "^1.12.6" } } diff --git a/src/skins/vector/css/gemini-scrollbar.css b/src/skins/vector/css/gemini-scrollbar.css deleted file mode 120000 index 4e3c83ba7d..0000000000 --- a/src/skins/vector/css/gemini-scrollbar.css +++ /dev/null @@ -1 +0,0 @@ -../../../../node_modules/react-gemini-scrollbar/node_modules/gemini-scrollbar/gemini-scrollbar.css \ No newline at end of file diff --git a/src/skins/vector/css/gfm.css b/src/skins/vector/css/gfm.css deleted file mode 120000 index 7940eacb53..0000000000 --- a/src/skins/vector/css/gfm.css +++ /dev/null @@ -1 +0,0 @@ -../../../../node_modules/gfm.css/gfm.css \ No newline at end of file diff --git a/src/skins/vector/css/github.css b/src/skins/vector/css/github.css deleted file mode 120000 index 1ca3fc5a7b..0000000000 --- a/src/skins/vector/css/github.css +++ /dev/null @@ -1 +0,0 @@ -../../../../node_modules/highlight.js/styles/github.css \ No newline at end of file diff --git a/src/vector/index.js b/src/vector/index.js index 00c22719ad..10057d99c7 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -16,6 +16,13 @@ limitations under the License. 'use strict'; +// CSS requires: just putting them here for now as CSS is going to be +// refactored soon anyway +require('../../vector/components.css'); +require('gemini-scrollbar/gemini-scrollbar.css'); +require('gfm.css/gfm.css'); +require('highlight.js/styles/github.css'); + var RunModernizrTests = require("./modernizr"); // this side-effects a global var React = require("react"); var ReactDOM = require("react-dom"); diff --git a/webpack.config.js b/webpack.config.js index 929e57d71b..3c71ed368a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,6 @@ var path = require('path'); var webpack = require('webpack'); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { @@ -9,6 +10,8 @@ module.exports = { loaders: [ { test: /\.json$/, loader: "json" }, { test: /\.js$/, loader: "babel", include: path.resolve('./src') }, + // css-raw-loader loads CSS but doesn't try to treat url()s as require()s + { test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, ] }, output: { @@ -37,6 +40,9 @@ module.exports = { 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) } + }), + new ExtractTextPlugin("bundle.css", { + allChunks: true }) ], devtool: 'source-map' From 349a88d6403e0cc4f602b3f2cb5cba13e43046bc Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 1 Dec 2015 18:37:51 +0000 Subject: [PATCH 10/16] stop clobbering our font colours --- src/skins/vector/css/molecules/EventTile.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/skins/vector/css/molecules/EventTile.css b/src/skins/vector/css/molecules/EventTile.css index d924cff6a7..7e1f1a26c8 100644 --- a/src/skins/vector/css/molecules/EventTile.css +++ b/src/skins/vector/css/molecules/EventTile.css @@ -75,6 +75,7 @@ limitations under the License. font-family: inherit ! important; white-space: normal ! important; line-height: inherit ! important; + color: inherit; font-size: 15px; } From cba27a7488327ce627cf04fe8288aa68a6db8a43 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 2 Dec 2015 01:37:26 +0000 Subject: [PATCH 11/16] erm, surely we need to actually run reskindex after all that? --- src/component-index.js | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/component-index.js b/src/component-index.js index bdca7c0f8b..764a7eb87d 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -23,26 +23,24 @@ limitations under the License. module.exports.components = require('matrix-react-sdk/lib/component-index').components; +module.exports.components['structures.BottomLeftMenu'] = require('./components/structures/BottomLeftMenu'); +module.exports.components['structures.CompatibilityPage'] = require('./components/structures/CompatibilityPage'); +module.exports.components['structures.LeftPanel'] = require('./components/structures/LeftPanel'); +module.exports.components['structures.RightPanel'] = require('./components/structures/RightPanel'); +module.exports.components['structures.RoomDirectory'] = require('./components/structures/RoomDirectory'); +module.exports.components['structures.RoomSubList'] = require('./components/structures/RoomSubList'); +module.exports.components['structures.ViewSource'] = require('./components/structures/ViewSource'); module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView'); module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner'); +module.exports.components['views.globals.MatrixToolbar'] = require('./components/views/globals/MatrixToolbar'); +module.exports.components['views.messages.DateSeparator'] = require('./components/views/messages/DateSeparator'); module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp'); +module.exports.components['views.messages.SenderProfile'] = require('./components/views/messages/SenderProfile'); +module.exports.components['views.rooms.BottomLeftMenuTile'] = require('./components/views/rooms/BottomLeftMenuTile'); +module.exports.components['views.rooms.MessageContextMenu'] = require('./components/views/rooms/MessageContextMenu'); module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView'); +module.exports.components['views.rooms.RoomDropTarget'] = require('./components/views/rooms/RoomDropTarget'); +module.exports.components['views.rooms.RoomTooltip'] = require('./components/views/rooms/RoomTooltip'); +module.exports.components['views.rooms.SearchBar'] = require('./components/views/rooms/SearchBar'); // Old, unported Vector views: -module.exports.components['molecules.BottomLeftMenu'] = require('./skins/vector/views/molecules/BottomLeftMenu'); -module.exports.components['molecules.BottomLeftMenuTile'] = require('./skins/vector/views/molecules/BottomLeftMenuTile'); -module.exports.components['molecules.DateSeparator'] = require('./skins/vector/views/molecules/DateSeparator'); -module.exports.components['molecules.MatrixToolbar'] = require('./skins/vector/views/molecules/MatrixToolbar'); -module.exports.components['molecules.MessageContextMenu'] = require('./skins/vector/views/molecules/MessageContextMenu'); -module.exports.components['molecules.RoomCreate'] = require('./skins/vector/views/molecules/RoomCreate'); -module.exports.components['molecules.RoomDropTarget'] = require('./skins/vector/views/molecules/RoomDropTarget'); -module.exports.components['molecules.RoomTooltip'] = require('./skins/vector/views/molecules/RoomTooltip'); -module.exports.components['molecules.SearchBar'] = require('./skins/vector/views/molecules/SearchBar'); -module.exports.components['molecules.SenderProfile'] = require('./skins/vector/views/molecules/SenderProfile'); -module.exports.components['organisms.LeftPanel'] = require('./skins/vector/views/organisms/LeftPanel'); -module.exports.components['organisms.Notifier'] = require('./skins/vector/views/organisms/Notifier'); -module.exports.components['organisms.RightPanel'] = require('./skins/vector/views/organisms/RightPanel'); -module.exports.components['organisms.RoomDirectory'] = require('./skins/vector/views/organisms/RoomDirectory'); -module.exports.components['organisms.RoomSubList'] = require('./skins/vector/views/organisms/RoomSubList'); -module.exports.components['organisms.ViewSource'] = require('./skins/vector/views/organisms/ViewSource'); -module.exports.components['pages.CompatibilityPage'] = require('./skins/vector/views/pages/CompatibilityPage'); From d837d02ac9dd9d387bca504be44ef554aa3926a5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 2 Dec 2015 09:26:12 +0000 Subject: [PATCH 12/16] Still add highlight.js as an import as we symlink directly to the node_module for github.css :/ - Fixes ENOENT --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0598201d5b..33141e8256 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "filesize": "^3.1.2", "flux": "~2.0.3", "gfm.css": "^1.1.1", + "highlight.js": "^9.0.0", "linkifyjs": "^2.0.0-beta.4", "matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop", "matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop", From de144cf4cd0949406efb3283021777e1f9d7677b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 2 Dec 2015 15:13:34 +0000 Subject: [PATCH 13/16] uh, actually include Open Sans webfont :-/ --- vector/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/index.html b/vector/index.html index d86466797d..b46cf638b2 100644 --- a/vector/index.html +++ b/vector/index.html @@ -3,7 +3,7 @@ Vector - + From c603c1e37f38675ef8a1b226157820f205df297b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 2 Dec 2015 17:37:19 +0000 Subject: [PATCH 14/16] dezalgo typing notigs on chrome --- src/skins/vector/css/organisms/RoomView.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css index 81b0ef280f..f586806f7c 100644 --- a/src/skins/vector/css/organisms/RoomView.css +++ b/src/skins/vector/css/organisms/RoomView.css @@ -166,6 +166,7 @@ limitations under the License. max-width: 960px; margin: auto; min-height: 36px; + overflow-y: hidden; } .mx_RoomView_statusAreaBox_line { From 2d2386ace5bdb923eaf123ff5dbf79f6f33fbeb5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 2 Dec 2015 18:19:03 +0000 Subject: [PATCH 15/16] Move upload bar CSS to new classes, and move into new, temporary place (since there is no new-style structure yet). --- src/controllers/organisms/RoomView.js | 736 +++++++++++++++++++ src/skins/vector/css/MOVE_ME/UploadBar.css | 42 ++ src/skins/vector/css/organisms/RoomView.css | 37 - src/skins/vector/views/organisms/RoomView.js | 312 ++++++++ 4 files changed, 1090 insertions(+), 37 deletions(-) create mode 100644 src/controllers/organisms/RoomView.js create mode 100644 src/skins/vector/css/MOVE_ME/UploadBar.css create mode 100644 src/skins/vector/views/organisms/RoomView.js diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js new file mode 100644 index 0000000000..0e6d5af37c --- /dev/null +++ b/src/controllers/organisms/RoomView.js @@ -0,0 +1,736 @@ +/* +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. +*/ + +var Matrix = require("matrix-js-sdk"); +var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); +var React = require("react"); +var ReactDOM = require("react-dom"); +var q = require("q"); +var ContentMessages = require("matrix-react-sdk/lib//ContentMessages"); +var WhoIsTyping = require("matrix-react-sdk/lib/WhoIsTyping"); +var Modal = require("matrix-react-sdk/lib/Modal"); +var sdk = require('matrix-react-sdk/lib/index'); +var CallHandler = require('matrix-react-sdk/lib/CallHandler'); +var VectorConferenceHandler = require('../../modules/VectorConferenceHandler'); +var Resend = require("../../Resend"); + +var dis = require("matrix-react-sdk/lib/dispatcher"); + +var PAGINATE_SIZE = 20; +var INITIAL_SIZE = 20; + +module.exports = { + getInitialState: function() { + var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null; + return { + room: room, + messageCap: INITIAL_SIZE, + editingRoomSettings: false, + uploadingRoomSettings: false, + numUnreadMessages: 0, + draggingFile: false, + searching: false, + searchResults: null, + syncState: MatrixClientPeg.get().getSyncState(), + hasUnsentMessages: this._hasUnsentMessages(room) + } + }, + + componentWillMount: function() { + this.dispatcherRef = dis.register(this.onAction); + MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); + MatrixClientPeg.get().on("Room.name", this.onRoomName); + MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); + MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); + MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); + MatrixClientPeg.get().on("sync", this.onSyncStateChange); + this.atBottom = true; + }, + + componentWillUnmount: function() { + if (this.refs.messagePanel) { + var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); + messagePanel.removeEventListener('drop', this.onDrop); + messagePanel.removeEventListener('dragover', this.onDragOver); + messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd); + messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd); + } + dis.unregister(this.dispatcherRef); + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); + MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); + MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); + MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping); + MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); + MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); + } + }, + + onAction: function(payload) { + switch (payload.action) { + case 'message_send_failed': + case 'message_sent': + this.setState({ + hasUnsentMessages: this._hasUnsentMessages(this.state.room) + }); + case 'message_resend_started': + this.setState({ + room: MatrixClientPeg.get().getRoom(this.props.roomId) + }); + this.forceUpdate(); + break; + case 'notifier_enabled': + this.forceUpdate(); + break; + case 'call_state': + if (CallHandler.getCallForRoom(this.props.roomId)) { + // Call state has changed so we may be loading video elements + // which will obscure the message log. + // scroll to bottom + var scrollNode = this._getScrollNode(); + if (scrollNode) { + scrollNode.scrollTop = scrollNode.scrollHeight; + } + } + + // possibly remove the conf call notification if we're now in + // the conf + this._updateConfCallNotification(); + break; + case 'user_activity': + this.sendReadReceipt(); + break; + case 'upload_failed': + case 'upload_started': + case 'upload_finished': + } + }, + + _getScrollNode: function() { + var panel = ReactDOM.findDOMNode(this.refs.messagePanel); + if (!panel) return null; + + if (panel.classList.contains('gm-prevented')) { + return panel; + } else { + return panel.children[2]; // XXX: Fragile! + } + }, + + onSyncStateChange: function(state) { + this.setState({ + syncState: state + }); + }, + + // MatrixRoom still showing the messages from the old room? + // Set the key to the room_id. Sadly you can no longer get at + // the key from inside the component, or we'd check this in code. + /*componentWillReceiveProps: function(props) { + },*/ + + onRoomTimeline: function(ev, room, toStartOfTimeline) { + if (!this.isMounted()) return; + + // ignore anything that comes in whilst paginating: we get one + // event for each new matrix event so this would cause a huge + // number of UI updates. Just update the UI when the paginate + // call returns. + if (this.state.paginating) return; + + // no point handling anything while we're waiting for the join to finish: + // we'll only be showing a spinner. + if (this.state.joining) return; + if (room.roomId != this.props.roomId) return; + + var scrollNode = this._getScrollNode(); + if (scrollNode) { + this.atBottom = ( + scrollNode.scrollHeight - scrollNode.scrollTop <= + (scrollNode.clientHeight + 150) // 150? + ); + } + + var currentUnread = this.state.numUnreadMessages; + if (!toStartOfTimeline && + (ev.getSender() !== MatrixClientPeg.get().credentials.userId)) { + // update unread count when scrolled up + if (this.atBottom) { + currentUnread = 0; + } + else { + currentUnread += 1; + } + } + + + this.setState({ + room: MatrixClientPeg.get().getRoom(this.props.roomId), + numUnreadMessages: currentUnread + }); + + if (toStartOfTimeline && !this.state.paginating) { + this.fillSpace(); + } + }, + + onRoomName: function(room) { + if (room.roomId == this.props.roomId) { + this.setState({ + room: room + }); + } + }, + + onRoomReceipt: function(receiptEvent, room) { + if (room.roomId == this.props.roomId) { + this.forceUpdate(); + } + }, + + onRoomMemberTyping: function(ev, member) { + this.forceUpdate(); + }, + + onRoomStateMember: function(ev, state, member) { + if (member.roomId !== this.props.roomId || + member.userId !== VectorConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { + return; + } + this._updateConfCallNotification(); + }, + + _hasUnsentMessages: function(room) { + return this._getUnsentMessages(room).length > 0; + }, + + _getUnsentMessages: function(room) { + if (!room) { return []; } + // TODO: It would be nice if the JS SDK provided nicer constant-time + // constructs rather than O(N) (N=num msgs) on this. + return room.timeline.filter(function(ev) { + return ev.status === Matrix.EventStatus.NOT_SENT; + }); + }, + + _updateConfCallNotification: function() { + var room = MatrixClientPeg.get().getRoom(this.props.roomId); + if (!room) return; + var confMember = room.getMember( + VectorConferenceHandler.getConferenceUserIdForRoom(this.props.roomId) + ); + + if (!confMember) { + return; + } + var confCall = VectorConferenceHandler.getConferenceCallForRoom(confMember.roomId); + + // A conf call notification should be displayed if there is an ongoing + // conf call but this cilent isn't a part of it. + this.setState({ + displayConfCallNotification: ( + (!confCall || confCall.call_state === "ended") && + confMember.membership === "join" + ) + }); + }, + + componentDidMount: function() { + if (this.refs.messagePanel) { + var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); + + messagePanel.addEventListener('drop', this.onDrop); + messagePanel.addEventListener('dragover', this.onDragOver); + messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd); + messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd); + + var messageWrapperScroll = this._getScrollNode(); + + messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; + + this.sendReadReceipt(); + + this.fillSpace(); + } + + this._updateConfCallNotification(); + }, + + componentDidUpdate: function() { + if (!this.refs.messagePanel) return; + + var messageWrapperScroll = this._getScrollNode(); + + if (this.state.paginating && !this.waiting_for_paginate) { + var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight; + messageWrapperScroll.scrollTop += heightGained; + this.oldScrollHeight = undefined; + if (!this.fillSpace()) { + this.setState({paginating: false}); + } + } else if (this.atBottom) { + messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; + if (this.state.numUnreadMessages !== 0) { + this.setState({numUnreadMessages: 0}); + } + } + }, + + fillSpace: function() { + if (!this.refs.messagePanel) return; + if (this.state.searchResults) return; // TODO: paginate search results + var messageWrapperScroll = this._getScrollNode(); + if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) { + this.setState({paginating: true}); + + this.oldScrollHeight = messageWrapperScroll.scrollHeight; + + if (this.state.messageCap < this.state.room.timeline.length) { + this.waiting_for_paginate = false; + var cap = Math.min(this.state.messageCap + PAGINATE_SIZE, this.state.room.timeline.length); + this.setState({messageCap: cap, paginating: true}); + } else { + this.waiting_for_paginate = true; + var cap = this.state.messageCap + PAGINATE_SIZE; + this.setState({messageCap: cap, paginating: true}); + var self = this; + MatrixClientPeg.get().scrollback(this.state.room, PAGINATE_SIZE).finally(function() { + self.waiting_for_paginate = false; + if (self.isMounted()) { + self.setState({ + room: MatrixClientPeg.get().getRoom(self.props.roomId) + }); + } + // wait and set paginating to false when the component updates + }); + } + + return true; + } + return false; + }, + + onResendAllClick: function() { + var eventsToResend = this._getUnsentMessages(this.state.room); + eventsToResend.forEach(function(event) { + Resend.resend(event); + }); + }, + + onJoinButtonClicked: function(ev) { + var self = this; + MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() { + self.setState({ + joining: false, + room: MatrixClientPeg.get().getRoom(self.props.roomId) + }); + }, function(error) { + self.setState({ + joining: false, + joinError: error + }); + }); + this.setState({ + joining: true + }); + }, + + onMessageListScroll: function(ev) { + if (this.refs.messagePanel) { + var messageWrapperScroll = this._getScrollNode(); + var wasAtBottom = this.atBottom; + this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1; + if (this.atBottom && !wasAtBottom) { + this.forceUpdate(); // remove unread msg count + } + } + if (!this.state.paginating) this.fillSpace(); + }, + + onDragOver: function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + + ev.dataTransfer.dropEffect = 'none'; + + var items = ev.dataTransfer.items; + if (items.length == 1) { + if (items[0].kind == 'file') { + this.setState({ draggingFile : true }); + ev.dataTransfer.dropEffect = 'copy'; + } + } + }, + + onDrop: function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + this.setState({ draggingFile : false }); + var files = ev.dataTransfer.files; + if (files.length == 1) { + this.uploadFile(files[0]); + } + }, + + onDragLeaveOrEnd: function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + this.setState({ draggingFile : false }); + }, + + uploadFile: function(file) { + var self = this; + ContentMessages.sendContentToRoom( + file, this.props.roomId, MatrixClientPeg.get() + ).progress(function(ev) { + //console.log("Upload: "+ev.loaded+" / "+ev.total); + if (ev) { + self.setState({ + upload: { + fileName: file.name, + uploadedBytes: ev.loaded, + totalBytes: ev.total + } + }); + } + }).finally(function() { + self.setState({ + upload: undefined + }); + }).done(undefined, function(error) { + var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to upload file", + description: error.toString() + }); + }); + }, + + getWhoIsTypingString: function() { + return WhoIsTyping.whoIsTypingString(this.state.room); + }, + + onSearch: function(term, scope) { + var filter; + if (scope === "Room") { + filter = { + // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( + rooms: [ + this.props.roomId + ] + }; + } + + var self = this; + MatrixClientPeg.get().search({ + body: { + search_categories: { + room_events: { + search_term: term, + filter: filter, + order_by: "recent", + include_state: true, + groupings: { + group_by: [ + { + key: "room_id" + } + ] + }, + event_context: { + before_limit: 1, + after_limit: 1, + include_profile: true, + } + } + } + } + }).then(function(data) { + // for debugging: + // data.search_categories.room_events.highlights = ["hello", "everybody"]; + + var highlights; + if (data.search_categories.room_events.highlights && + data.search_categories.room_events.highlights.length > 0) + { + // postgres on synapse returns us precise details of the + // strings which actually got matched for highlighting. + // for overlapping highlights, favour longer (more specific) terms first + highlights = data.search_categories.room_events.highlights + .sort(function(a, b) { b.length - a.length }); + } + else { + // sqlite doesn't, so just try to highlight the literal search term + highlights = [ term ]; + } + + self.setState({ + highlights: highlights, + searchResults: data, + searchScope: scope, + }); + }, function(error) { + var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Search failed", + description: error.toString() + }); + }); + }, + + getEventTiles: function() { + var DateSeparator = sdk.getComponent('molecules.DateSeparator'); + var cli = MatrixClientPeg.get(); + + var ret = []; + var count = 0; + + var EventTile = sdk.getComponent('rooms.EventTile'); + var self = this; + + if (this.state.searchResults && + this.state.searchResults.search_categories.room_events.results && + this.state.searchResults.search_categories.room_events.groups) + { + // XXX: this dance is foul, due to the results API not directly returning sorted results + var results = this.state.searchResults.search_categories.room_events.results; + var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id; + + Object.keys(roomIdGroups) + .sort(function(a, b) { roomIdGroups[a].order - roomIdGroups[b].order }) // WHY NOT RETURN AN ORDERED ARRAY?!?!?! + .forEach(function(roomId) + { + // XXX: todo: merge overlapping results somehow? + // XXX: why doesn't searching on name work? + if (self.state.searchScope === 'All') { + ret.push(
  • Room: { cli.getRoom(roomId).name }

  • ); + } + + var resultList = roomIdGroups[roomId].results.map(function(eventId) { return results[eventId]; }); + for (var i = resultList.length - 1; i >= 0; i--) { + var ts1 = resultList[i].result.origin_server_ts; + ret.push(
  • ); // Rank: {resultList[i].rank} + var mxEv = new Matrix.MatrixEvent(resultList[i].result); + if (resultList[i].context.events_before[0]) { + var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]); + if (EventTile.haveTileForEvent(mxEv2)) { + ret.push(
  • ); + } + } + if (EventTile.haveTileForEvent(mxEv)) { + ret.push(
  • ); + } + if (resultList[i].context.events_after[0]) { + var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]); + if (EventTile.haveTileForEvent(mxEv2)) { + ret.push(
  • ); + } + } + } + }); + return ret; + } + + for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) { + var mxEv = this.state.room.timeline[i]; + + if (!EventTile.haveTileForEvent(mxEv)) { + continue; + } + + var continuation = false; + var last = false; + var dateSeparator = null; + if (i == this.state.room.timeline.length - 1) { + last = true; + } + if (i > 0 && count < this.state.messageCap - 1) { + if (this.state.room.timeline[i].sender && + this.state.room.timeline[i - 1].sender && + (this.state.room.timeline[i].sender.userId === + this.state.room.timeline[i - 1].sender.userId) && + (this.state.room.timeline[i].getType() == + this.state.room.timeline[i - 1].getType()) + ) + { + continuation = true; + } + + var ts0 = this.state.room.timeline[i - 1].getTs(); + var ts1 = this.state.room.timeline[i].getTs(); + if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) { + dateSeparator =
  • ; + continuation = false; + } + } + + if (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline + var ts1 = this.state.room.timeline[i].getTs(); + dateSeparator =
  • ; + continuation = false; + } + + ret.unshift( +
  • + ); + if (dateSeparator) { + ret.unshift(dateSeparator); + } + ++count; + } + return ret; + }, + + uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { + var old_name = this.state.room.name; + + var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); + if (old_topic) { + old_topic = old_topic.getContent().topic; + } else { + old_topic = ""; + } + + var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); + if (old_join_rule) { + old_join_rule = old_join_rule.getContent().join_rule; + } else { + old_join_rule = "invite"; + } + + var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); + if (old_history_visibility) { + old_history_visibility = old_history_visibility.getContent().history_visibility; + } else { + old_history_visibility = "shared"; + } + + var deferreds = []; + + if (old_name != new_name && new_name != undefined && new_name) { + deferreds.push( + MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) + ); + } + + if (old_topic != new_topic && new_topic != undefined) { + deferreds.push( + MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) + ); + } + + if (old_join_rule != new_join_rule && new_join_rule != undefined) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.join_rules", { + join_rule: new_join_rule, + }, "" + ) + ); + } + + if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.history_visibility", { + history_visibility: new_history_visibility, + }, "" + ) + ); + } + + if (new_power_levels) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + ) + ); + } + + if (deferreds.length) { + var self = this; + q.all(deferreds).fail(function(err) { + var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to set state", + description: err.toString() + }); + }).finally(function() { + self.setState({ + uploadingRoomSettings: false, + }); + }); + } else { + this.setState({ + editingRoomSettings: false, + uploadingRoomSettings: false, + }); + } + }, + + _collectEventNode: function(eventId, node) { + if (this.eventNodes == undefined) this.eventNodes = {}; + this.eventNodes[eventId] = node; + }, + + _indexForEventId(evId) { + for (var i = 0; i < this.state.room.timeline.length; ++i) { + if (evId == this.state.room.timeline[i].getId()) { + return i; + } + } + return null; + }, + + sendReadReceipt: function() { + if (!this.state.room) return; + var currentReadUpToEventId = this.state.room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId); + var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); + + var lastReadEventIndex = this._getLastDisplayedEventIndexIgnoringOwn(); + if (lastReadEventIndex === null) return; + + if (lastReadEventIndex > currentReadUpToEventIndex) { + MatrixClientPeg.get().sendReadReceipt(this.state.room.timeline[lastReadEventIndex]); + } + }, + + _getLastDisplayedEventIndexIgnoringOwn: function() { + if (this.eventNodes === undefined) return null; + + var messageWrapper = this.refs.messagePanel; + if (messageWrapper === undefined) return null; + var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect(); + + for (var i = this.state.room.timeline.length-1; i >= 0; --i) { + var ev = this.state.room.timeline[i]; + + if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) { + continue; + } + + var node = this.eventNodes[ev.getId()]; + if (!node) continue; + + var boundingRect = node.getBoundingClientRect(); + + if (boundingRect.bottom < wrapperRect.bottom) { + return i; + } + } + return null; + } +}; diff --git a/src/skins/vector/css/MOVE_ME/UploadBar.css b/src/skins/vector/css/MOVE_ME/UploadBar.css new file mode 100644 index 0000000000..9a12689f5c --- /dev/null +++ b/src/skins/vector/css/MOVE_ME/UploadBar.css @@ -0,0 +1,42 @@ +.mx_UploadBar { + position: relative; +} + +.mx_UploadBar_uploadProgressOuter { + height: 4px; + margin-left: 63px; + margin-top: -1px; +} + +.mx_UploadBar_uploadProgressInner { + background-color: #76cfa6; + height: 4px; +} + +.mx_UploadBar_uploadFilename { + margin-top: 5px; + margin-left: 65px; + opacity: 0.5; + color: #4a4a4a; +} + +.mx_UploadBar_uploadIcon { + float: left; + margin-top: 1px; + margin-left: 14px; +} + +.mx_UploadBar_uploadCancel { + float: right; + margin-top: 5px; + margin-right: 10px; + position: relative; + z-index: 1; +} + +.mx_UploadBar_uploadBytes { + float: right; + margin-top: 5px; + margin-right: 30px; + color: #76cfa6; +} diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css index 81b0ef280f..adea5b4a37 100644 --- a/src/skins/vector/css/organisms/RoomView.css +++ b/src/skins/vector/css/organisms/RoomView.css @@ -241,43 +241,6 @@ limitations under the License. margin-right: 2px; } -.mx_RoomView_uploadProgressOuter { - height: 4px; - margin-left: 63px; - margin-top: -1px; -} - -.mx_RoomView_uploadProgressInner { - background-color: #76cfa6; - height: 4px; -} - -.mx_RoomView_uploadFilename { - margin-top: 5px; - margin-left: 65px; - opacity: 0.5; - color: #4a4a4a; -} - -.mx_RoomView_uploadIcon { - float: left; - margin-top: 1px; - margin-left: 14px; -} - -.mx_RoomView_uploadCancel { - float: right; - margin-top: 5px; - margin-right: 10px; -} - -.mx_RoomView_uploadBytes { - float: right; - margin-top: 5px; - margin-right: 30px; - color: #76cfa6; -} - .mx_RoomView_ongoingConfCallNotification { width: 100%; text-align: center; diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js new file mode 100644 index 0000000000..8ccd8b9017 --- /dev/null +++ b/src/skins/vector/views/organisms/RoomView.js @@ -0,0 +1,312 @@ +/* +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. +*/ + +'use strict'; + +var React = require('react'); +var ReactDOM = require('react-dom'); + +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var dis = require('matrix-react-sdk/lib/dispatcher'); +var ContentMessages = require('matrix-react-sdk/lib/ContentMessages'); + +var sdk = require('matrix-react-sdk') +var classNames = require("classnames"); +var filesize = require('filesize'); + +var GeminiScrollbar = require('react-gemini-scrollbar'); +var RoomViewController = require('../../../../controllers/organisms/RoomView') +var VectorConferenceHandler = require('../../../../modules/VectorConferenceHandler'); + +module.exports = React.createClass({ + displayName: 'RoomView', + mixins: [RoomViewController], + + onSettingsClick: function() { + this.setState({editingRoomSettings: true}); + }, + + onSaveClick: function() { + this.setState({ + editingRoomSettings: false, + uploadingRoomSettings: true, + }); + + var new_name = this.refs.header.getRoomName(); + var new_topic = this.refs.room_settings.getTopic(); + var new_join_rule = this.refs.room_settings.getJoinRules(); + var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); + var new_power_levels = this.refs.room_settings.getPowerLevels(); + + this.uploadNewState( + new_name, + new_topic, + new_join_rule, + new_history_visibility, + new_power_levels + ); + }, + + onCancelClick: function() { + this.setState(this.getInitialState()); + }, + + onRejectButtonClicked: function(ev) { + var self = this; + this.setState({ + rejecting: true + }); + MatrixClientPeg.get().leave(this.props.roomId).done(function() { + dis.dispatch({ action: 'view_next_room' }); + self.setState({ + rejecting: false + }); + }, function(err) { + console.error("Failed to reject invite: %s", err); + self.setState({ + rejecting: false, + rejectError: err + }); + }); + }, + + onSearchClick: function() { + this.setState({ searching: true }); + }, + + onConferenceNotificationClick: function() { + dis.dispatch({ + action: 'place_call', + type: "video", + room_id: this.props.roomId + }); + }, + + getUnreadMessagesString: function() { + if (!this.state.numUnreadMessages) { + return ""; + } + return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : ""); + }, + + scrollToBottom: function() { + var scrollNode = this._getScrollNode(); + if (!scrollNode) return; + scrollNode.scrollTop = scrollNode.scrollHeight; + }, + + render: function() { + var RoomHeader = sdk.getComponent('rooms.RoomHeader'); + var MessageComposer = sdk.getComponent('rooms.MessageComposer'); + var CallView = sdk.getComponent("voip.CallView"); + var RoomSettings = sdk.getComponent("rooms.RoomSettings"); + var SearchBar = sdk.getComponent("molecules.SearchBar"); + + if (!this.state.room) { + if (this.props.roomId) { + return ( +
    + +
    + ); + } else { + return ( +
    + ); + } + } + + var myUserId = MatrixClientPeg.get().credentials.userId; + if (this.state.room.currentState.members[myUserId].membership == 'invite') { + if (this.state.joining || this.state.rejecting) { + var Loader = sdk.getComponent("elements.Spinner"); + return ( +
    + +
    + ); + } else { + var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event; + // XXX: Leaving this intentionally basic for now because invites are about to change totally + var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; + var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : ""; + return ( +
    + +
    +
    {inviteEvent.user_id} has invited you to a room
    +
    + + +
    {joinErrorText}
    +
    {rejectErrorText}
    +
    +
    + ); + } + } else { + var scrollheader_classes = classNames({ + mx_RoomView_scrollheader: true, + loading: this.state.paginating + }); + + var statusBar = ( +
    + ); + + // for testing UI... + // this.state.upload = { + // uploadedBytes: 123493, + // totalBytes: 347534, + // fileName: "testing_fooble.jpg", + // } + + if (ContentMessages.getCurrentUploads().length > 0) { + var UploadBar = sdk.getComponent('statusbar/UploadBar'); + statusBar = + } else { + var typingString = this.getWhoIsTypingString(); + //typingString = "Testing typing..."; + var unreadMsgs = this.getUnreadMessagesString(); + // no conn bar trumps unread count since you can't get unread messages + // without a connection! (technically may already have some but meh) + // It also trumps the "some not sent" msg since you can't resend without + // a connection! + if (this.state.syncState === "ERROR") { + statusBar = ( +
    + /!\ +
    +
    + Connectivity to the server has been lost. +
    +
    + Sent messages will be stored until your connection has returned. +
    +
    +
    + ); + } + else if (this.state.hasUnsentMessages) { + statusBar = ( +
    + /!\ +
    +
    + Some of your messages have not been sent. +
    +
    + + Resend all now + or select individual messages to re-send. +
    +
    +
    + ); + } + // unread count trumps who is typing since the unread count is only + // set when you've scrolled up + else if (unreadMsgs) { + statusBar = ( +
    + + {unreadMsgs} +
    + ); + } + else if (typingString) { + statusBar = ( +
    +
    ...
    + {typingString} +
    + ); + } + } + + var aux = null; + if (this.state.editingRoomSettings) { + aux = ; + } + else if (this.state.uploadingRoomSettings) { + var Loader = sdk.getComponent("elements.Spinner"); + aux = ; + } + else if (this.state.searching) { + aux = ; + } + + var conferenceCallNotification = null; + if (this.state.displayConfCallNotification) { + var supportedText; + if (!MatrixClientPeg.get().supportsVoip()) { + supportedText = " (unsupported)"; + } + conferenceCallNotification = ( +
    + Ongoing conference call {supportedText} +
    + ); + } + + var fileDropTarget = null; + if (this.state.draggingFile) { + fileDropTarget =
    +
    + Drop File Here
    + Drop File Here +
    +
    ; + } + + var messageComposer; + if (!this.state.searchResults) { + messageComposer = + + } + + return ( +
    + +
    + + { conferenceCallNotification } + { aux } +
    + +
    + { fileDropTarget } +
      +
    1. +
    2. + {this.getEventTiles()} +
    +
    +
    +
    +
    +
    + { this.state.searchResults ? null : statusBar } +
    +
    + { messageComposer } +
    + ); + } + }, +}); From 547f59b1bcfe2ce34bfd34a6af2e3a8f063e94a7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 3 Dec 2015 10:00:29 +0000 Subject: [PATCH 16/16] Oops, don't re-add these. --- src/controllers/organisms/RoomView.js | 736 ------------------- src/skins/vector/views/organisms/RoomView.js | 312 -------- 2 files changed, 1048 deletions(-) delete mode 100644 src/controllers/organisms/RoomView.js delete mode 100644 src/skins/vector/views/organisms/RoomView.js diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js deleted file mode 100644 index 0e6d5af37c..0000000000 --- a/src/controllers/organisms/RoomView.js +++ /dev/null @@ -1,736 +0,0 @@ -/* -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. -*/ - -var Matrix = require("matrix-js-sdk"); -var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); -var React = require("react"); -var ReactDOM = require("react-dom"); -var q = require("q"); -var ContentMessages = require("matrix-react-sdk/lib//ContentMessages"); -var WhoIsTyping = require("matrix-react-sdk/lib/WhoIsTyping"); -var Modal = require("matrix-react-sdk/lib/Modal"); -var sdk = require('matrix-react-sdk/lib/index'); -var CallHandler = require('matrix-react-sdk/lib/CallHandler'); -var VectorConferenceHandler = require('../../modules/VectorConferenceHandler'); -var Resend = require("../../Resend"); - -var dis = require("matrix-react-sdk/lib/dispatcher"); - -var PAGINATE_SIZE = 20; -var INITIAL_SIZE = 20; - -module.exports = { - getInitialState: function() { - var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null; - return { - room: room, - messageCap: INITIAL_SIZE, - editingRoomSettings: false, - uploadingRoomSettings: false, - numUnreadMessages: 0, - draggingFile: false, - searching: false, - searchResults: null, - syncState: MatrixClientPeg.get().getSyncState(), - hasUnsentMessages: this._hasUnsentMessages(room) - } - }, - - componentWillMount: function() { - this.dispatcherRef = dis.register(this.onAction); - MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); - MatrixClientPeg.get().on("Room.name", this.onRoomName); - MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); - MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); - MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); - MatrixClientPeg.get().on("sync", this.onSyncStateChange); - this.atBottom = true; - }, - - componentWillUnmount: function() { - if (this.refs.messagePanel) { - var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); - messagePanel.removeEventListener('drop', this.onDrop); - messagePanel.removeEventListener('dragover', this.onDragOver); - messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd); - messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd); - } - dis.unregister(this.dispatcherRef); - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); - MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); - MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); - MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping); - MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); - MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); - } - }, - - onAction: function(payload) { - switch (payload.action) { - case 'message_send_failed': - case 'message_sent': - this.setState({ - hasUnsentMessages: this._hasUnsentMessages(this.state.room) - }); - case 'message_resend_started': - this.setState({ - room: MatrixClientPeg.get().getRoom(this.props.roomId) - }); - this.forceUpdate(); - break; - case 'notifier_enabled': - this.forceUpdate(); - break; - case 'call_state': - if (CallHandler.getCallForRoom(this.props.roomId)) { - // Call state has changed so we may be loading video elements - // which will obscure the message log. - // scroll to bottom - var scrollNode = this._getScrollNode(); - if (scrollNode) { - scrollNode.scrollTop = scrollNode.scrollHeight; - } - } - - // possibly remove the conf call notification if we're now in - // the conf - this._updateConfCallNotification(); - break; - case 'user_activity': - this.sendReadReceipt(); - break; - case 'upload_failed': - case 'upload_started': - case 'upload_finished': - } - }, - - _getScrollNode: function() { - var panel = ReactDOM.findDOMNode(this.refs.messagePanel); - if (!panel) return null; - - if (panel.classList.contains('gm-prevented')) { - return panel; - } else { - return panel.children[2]; // XXX: Fragile! - } - }, - - onSyncStateChange: function(state) { - this.setState({ - syncState: state - }); - }, - - // MatrixRoom still showing the messages from the old room? - // Set the key to the room_id. Sadly you can no longer get at - // the key from inside the component, or we'd check this in code. - /*componentWillReceiveProps: function(props) { - },*/ - - onRoomTimeline: function(ev, room, toStartOfTimeline) { - if (!this.isMounted()) return; - - // ignore anything that comes in whilst paginating: we get one - // event for each new matrix event so this would cause a huge - // number of UI updates. Just update the UI when the paginate - // call returns. - if (this.state.paginating) return; - - // no point handling anything while we're waiting for the join to finish: - // we'll only be showing a spinner. - if (this.state.joining) return; - if (room.roomId != this.props.roomId) return; - - var scrollNode = this._getScrollNode(); - if (scrollNode) { - this.atBottom = ( - scrollNode.scrollHeight - scrollNode.scrollTop <= - (scrollNode.clientHeight + 150) // 150? - ); - } - - var currentUnread = this.state.numUnreadMessages; - if (!toStartOfTimeline && - (ev.getSender() !== MatrixClientPeg.get().credentials.userId)) { - // update unread count when scrolled up - if (this.atBottom) { - currentUnread = 0; - } - else { - currentUnread += 1; - } - } - - - this.setState({ - room: MatrixClientPeg.get().getRoom(this.props.roomId), - numUnreadMessages: currentUnread - }); - - if (toStartOfTimeline && !this.state.paginating) { - this.fillSpace(); - } - }, - - onRoomName: function(room) { - if (room.roomId == this.props.roomId) { - this.setState({ - room: room - }); - } - }, - - onRoomReceipt: function(receiptEvent, room) { - if (room.roomId == this.props.roomId) { - this.forceUpdate(); - } - }, - - onRoomMemberTyping: function(ev, member) { - this.forceUpdate(); - }, - - onRoomStateMember: function(ev, state, member) { - if (member.roomId !== this.props.roomId || - member.userId !== VectorConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { - return; - } - this._updateConfCallNotification(); - }, - - _hasUnsentMessages: function(room) { - return this._getUnsentMessages(room).length > 0; - }, - - _getUnsentMessages: function(room) { - if (!room) { return []; } - // TODO: It would be nice if the JS SDK provided nicer constant-time - // constructs rather than O(N) (N=num msgs) on this. - return room.timeline.filter(function(ev) { - return ev.status === Matrix.EventStatus.NOT_SENT; - }); - }, - - _updateConfCallNotification: function() { - var room = MatrixClientPeg.get().getRoom(this.props.roomId); - if (!room) return; - var confMember = room.getMember( - VectorConferenceHandler.getConferenceUserIdForRoom(this.props.roomId) - ); - - if (!confMember) { - return; - } - var confCall = VectorConferenceHandler.getConferenceCallForRoom(confMember.roomId); - - // A conf call notification should be displayed if there is an ongoing - // conf call but this cilent isn't a part of it. - this.setState({ - displayConfCallNotification: ( - (!confCall || confCall.call_state === "ended") && - confMember.membership === "join" - ) - }); - }, - - componentDidMount: function() { - if (this.refs.messagePanel) { - var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); - - messagePanel.addEventListener('drop', this.onDrop); - messagePanel.addEventListener('dragover', this.onDragOver); - messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd); - messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd); - - var messageWrapperScroll = this._getScrollNode(); - - messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; - - this.sendReadReceipt(); - - this.fillSpace(); - } - - this._updateConfCallNotification(); - }, - - componentDidUpdate: function() { - if (!this.refs.messagePanel) return; - - var messageWrapperScroll = this._getScrollNode(); - - if (this.state.paginating && !this.waiting_for_paginate) { - var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight; - messageWrapperScroll.scrollTop += heightGained; - this.oldScrollHeight = undefined; - if (!this.fillSpace()) { - this.setState({paginating: false}); - } - } else if (this.atBottom) { - messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; - if (this.state.numUnreadMessages !== 0) { - this.setState({numUnreadMessages: 0}); - } - } - }, - - fillSpace: function() { - if (!this.refs.messagePanel) return; - if (this.state.searchResults) return; // TODO: paginate search results - var messageWrapperScroll = this._getScrollNode(); - if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) { - this.setState({paginating: true}); - - this.oldScrollHeight = messageWrapperScroll.scrollHeight; - - if (this.state.messageCap < this.state.room.timeline.length) { - this.waiting_for_paginate = false; - var cap = Math.min(this.state.messageCap + PAGINATE_SIZE, this.state.room.timeline.length); - this.setState({messageCap: cap, paginating: true}); - } else { - this.waiting_for_paginate = true; - var cap = this.state.messageCap + PAGINATE_SIZE; - this.setState({messageCap: cap, paginating: true}); - var self = this; - MatrixClientPeg.get().scrollback(this.state.room, PAGINATE_SIZE).finally(function() { - self.waiting_for_paginate = false; - if (self.isMounted()) { - self.setState({ - room: MatrixClientPeg.get().getRoom(self.props.roomId) - }); - } - // wait and set paginating to false when the component updates - }); - } - - return true; - } - return false; - }, - - onResendAllClick: function() { - var eventsToResend = this._getUnsentMessages(this.state.room); - eventsToResend.forEach(function(event) { - Resend.resend(event); - }); - }, - - onJoinButtonClicked: function(ev) { - var self = this; - MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() { - self.setState({ - joining: false, - room: MatrixClientPeg.get().getRoom(self.props.roomId) - }); - }, function(error) { - self.setState({ - joining: false, - joinError: error - }); - }); - this.setState({ - joining: true - }); - }, - - onMessageListScroll: function(ev) { - if (this.refs.messagePanel) { - var messageWrapperScroll = this._getScrollNode(); - var wasAtBottom = this.atBottom; - this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1; - if (this.atBottom && !wasAtBottom) { - this.forceUpdate(); // remove unread msg count - } - } - if (!this.state.paginating) this.fillSpace(); - }, - - onDragOver: function(ev) { - ev.stopPropagation(); - ev.preventDefault(); - - ev.dataTransfer.dropEffect = 'none'; - - var items = ev.dataTransfer.items; - if (items.length == 1) { - if (items[0].kind == 'file') { - this.setState({ draggingFile : true }); - ev.dataTransfer.dropEffect = 'copy'; - } - } - }, - - onDrop: function(ev) { - ev.stopPropagation(); - ev.preventDefault(); - this.setState({ draggingFile : false }); - var files = ev.dataTransfer.files; - if (files.length == 1) { - this.uploadFile(files[0]); - } - }, - - onDragLeaveOrEnd: function(ev) { - ev.stopPropagation(); - ev.preventDefault(); - this.setState({ draggingFile : false }); - }, - - uploadFile: function(file) { - var self = this; - ContentMessages.sendContentToRoom( - file, this.props.roomId, MatrixClientPeg.get() - ).progress(function(ev) { - //console.log("Upload: "+ev.loaded+" / "+ev.total); - if (ev) { - self.setState({ - upload: { - fileName: file.name, - uploadedBytes: ev.loaded, - totalBytes: ev.total - } - }); - } - }).finally(function() { - self.setState({ - upload: undefined - }); - }).done(undefined, function(error) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to upload file", - description: error.toString() - }); - }); - }, - - getWhoIsTypingString: function() { - return WhoIsTyping.whoIsTypingString(this.state.room); - }, - - onSearch: function(term, scope) { - var filter; - if (scope === "Room") { - filter = { - // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( - rooms: [ - this.props.roomId - ] - }; - } - - var self = this; - MatrixClientPeg.get().search({ - body: { - search_categories: { - room_events: { - search_term: term, - filter: filter, - order_by: "recent", - include_state: true, - groupings: { - group_by: [ - { - key: "room_id" - } - ] - }, - event_context: { - before_limit: 1, - after_limit: 1, - include_profile: true, - } - } - } - } - }).then(function(data) { - // for debugging: - // data.search_categories.room_events.highlights = ["hello", "everybody"]; - - var highlights; - if (data.search_categories.room_events.highlights && - data.search_categories.room_events.highlights.length > 0) - { - // postgres on synapse returns us precise details of the - // strings which actually got matched for highlighting. - // for overlapping highlights, favour longer (more specific) terms first - highlights = data.search_categories.room_events.highlights - .sort(function(a, b) { b.length - a.length }); - } - else { - // sqlite doesn't, so just try to highlight the literal search term - highlights = [ term ]; - } - - self.setState({ - highlights: highlights, - searchResults: data, - searchScope: scope, - }); - }, function(error) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Search failed", - description: error.toString() - }); - }); - }, - - getEventTiles: function() { - var DateSeparator = sdk.getComponent('molecules.DateSeparator'); - var cli = MatrixClientPeg.get(); - - var ret = []; - var count = 0; - - var EventTile = sdk.getComponent('rooms.EventTile'); - var self = this; - - if (this.state.searchResults && - this.state.searchResults.search_categories.room_events.results && - this.state.searchResults.search_categories.room_events.groups) - { - // XXX: this dance is foul, due to the results API not directly returning sorted results - var results = this.state.searchResults.search_categories.room_events.results; - var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id; - - Object.keys(roomIdGroups) - .sort(function(a, b) { roomIdGroups[a].order - roomIdGroups[b].order }) // WHY NOT RETURN AN ORDERED ARRAY?!?!?! - .forEach(function(roomId) - { - // XXX: todo: merge overlapping results somehow? - // XXX: why doesn't searching on name work? - if (self.state.searchScope === 'All') { - ret.push(
  • Room: { cli.getRoom(roomId).name }

  • ); - } - - var resultList = roomIdGroups[roomId].results.map(function(eventId) { return results[eventId]; }); - for (var i = resultList.length - 1; i >= 0; i--) { - var ts1 = resultList[i].result.origin_server_ts; - ret.push(
  • ); // Rank: {resultList[i].rank} - var mxEv = new Matrix.MatrixEvent(resultList[i].result); - if (resultList[i].context.events_before[0]) { - var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]); - if (EventTile.haveTileForEvent(mxEv2)) { - ret.push(
  • ); - } - } - if (EventTile.haveTileForEvent(mxEv)) { - ret.push(
  • ); - } - if (resultList[i].context.events_after[0]) { - var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]); - if (EventTile.haveTileForEvent(mxEv2)) { - ret.push(
  • ); - } - } - } - }); - return ret; - } - - for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) { - var mxEv = this.state.room.timeline[i]; - - if (!EventTile.haveTileForEvent(mxEv)) { - continue; - } - - var continuation = false; - var last = false; - var dateSeparator = null; - if (i == this.state.room.timeline.length - 1) { - last = true; - } - if (i > 0 && count < this.state.messageCap - 1) { - if (this.state.room.timeline[i].sender && - this.state.room.timeline[i - 1].sender && - (this.state.room.timeline[i].sender.userId === - this.state.room.timeline[i - 1].sender.userId) && - (this.state.room.timeline[i].getType() == - this.state.room.timeline[i - 1].getType()) - ) - { - continuation = true; - } - - var ts0 = this.state.room.timeline[i - 1].getTs(); - var ts1 = this.state.room.timeline[i].getTs(); - if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) { - dateSeparator =
  • ; - continuation = false; - } - } - - if (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline - var ts1 = this.state.room.timeline[i].getTs(); - dateSeparator =
  • ; - continuation = false; - } - - ret.unshift( -
  • - ); - if (dateSeparator) { - ret.unshift(dateSeparator); - } - ++count; - } - return ret; - }, - - uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { - var old_name = this.state.room.name; - - var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); - if (old_topic) { - old_topic = old_topic.getContent().topic; - } else { - old_topic = ""; - } - - var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); - if (old_join_rule) { - old_join_rule = old_join_rule.getContent().join_rule; - } else { - old_join_rule = "invite"; - } - - var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); - if (old_history_visibility) { - old_history_visibility = old_history_visibility.getContent().history_visibility; - } else { - old_history_visibility = "shared"; - } - - var deferreds = []; - - if (old_name != new_name && new_name != undefined && new_name) { - deferreds.push( - MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) - ); - } - - if (old_topic != new_topic && new_topic != undefined) { - deferreds.push( - MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) - ); - } - - if (old_join_rule != new_join_rule && new_join_rule != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.join_rules", { - join_rule: new_join_rule, - }, "" - ) - ); - } - - if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.history_visibility", { - history_visibility: new_history_visibility, - }, "" - ) - ); - } - - if (new_power_levels) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.power_levels", new_power_levels, "" - ) - ); - } - - if (deferreds.length) { - var self = this; - q.all(deferreds).fail(function(err) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to set state", - description: err.toString() - }); - }).finally(function() { - self.setState({ - uploadingRoomSettings: false, - }); - }); - } else { - this.setState({ - editingRoomSettings: false, - uploadingRoomSettings: false, - }); - } - }, - - _collectEventNode: function(eventId, node) { - if (this.eventNodes == undefined) this.eventNodes = {}; - this.eventNodes[eventId] = node; - }, - - _indexForEventId(evId) { - for (var i = 0; i < this.state.room.timeline.length; ++i) { - if (evId == this.state.room.timeline[i].getId()) { - return i; - } - } - return null; - }, - - sendReadReceipt: function() { - if (!this.state.room) return; - var currentReadUpToEventId = this.state.room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId); - var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); - - var lastReadEventIndex = this._getLastDisplayedEventIndexIgnoringOwn(); - if (lastReadEventIndex === null) return; - - if (lastReadEventIndex > currentReadUpToEventIndex) { - MatrixClientPeg.get().sendReadReceipt(this.state.room.timeline[lastReadEventIndex]); - } - }, - - _getLastDisplayedEventIndexIgnoringOwn: function() { - if (this.eventNodes === undefined) return null; - - var messageWrapper = this.refs.messagePanel; - if (messageWrapper === undefined) return null; - var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect(); - - for (var i = this.state.room.timeline.length-1; i >= 0; --i) { - var ev = this.state.room.timeline[i]; - - if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) { - continue; - } - - var node = this.eventNodes[ev.getId()]; - if (!node) continue; - - var boundingRect = node.getBoundingClientRect(); - - if (boundingRect.bottom < wrapperRect.bottom) { - return i; - } - } - return null; - } -}; diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js deleted file mode 100644 index 8ccd8b9017..0000000000 --- a/src/skins/vector/views/organisms/RoomView.js +++ /dev/null @@ -1,312 +0,0 @@ -/* -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. -*/ - -'use strict'; - -var React = require('react'); -var ReactDOM = require('react-dom'); - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var dis = require('matrix-react-sdk/lib/dispatcher'); -var ContentMessages = require('matrix-react-sdk/lib/ContentMessages'); - -var sdk = require('matrix-react-sdk') -var classNames = require("classnames"); -var filesize = require('filesize'); - -var GeminiScrollbar = require('react-gemini-scrollbar'); -var RoomViewController = require('../../../../controllers/organisms/RoomView') -var VectorConferenceHandler = require('../../../../modules/VectorConferenceHandler'); - -module.exports = React.createClass({ - displayName: 'RoomView', - mixins: [RoomViewController], - - onSettingsClick: function() { - this.setState({editingRoomSettings: true}); - }, - - onSaveClick: function() { - this.setState({ - editingRoomSettings: false, - uploadingRoomSettings: true, - }); - - var new_name = this.refs.header.getRoomName(); - var new_topic = this.refs.room_settings.getTopic(); - var new_join_rule = this.refs.room_settings.getJoinRules(); - var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); - var new_power_levels = this.refs.room_settings.getPowerLevels(); - - this.uploadNewState( - new_name, - new_topic, - new_join_rule, - new_history_visibility, - new_power_levels - ); - }, - - onCancelClick: function() { - this.setState(this.getInitialState()); - }, - - onRejectButtonClicked: function(ev) { - var self = this; - this.setState({ - rejecting: true - }); - MatrixClientPeg.get().leave(this.props.roomId).done(function() { - dis.dispatch({ action: 'view_next_room' }); - self.setState({ - rejecting: false - }); - }, function(err) { - console.error("Failed to reject invite: %s", err); - self.setState({ - rejecting: false, - rejectError: err - }); - }); - }, - - onSearchClick: function() { - this.setState({ searching: true }); - }, - - onConferenceNotificationClick: function() { - dis.dispatch({ - action: 'place_call', - type: "video", - room_id: this.props.roomId - }); - }, - - getUnreadMessagesString: function() { - if (!this.state.numUnreadMessages) { - return ""; - } - return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : ""); - }, - - scrollToBottom: function() { - var scrollNode = this._getScrollNode(); - if (!scrollNode) return; - scrollNode.scrollTop = scrollNode.scrollHeight; - }, - - render: function() { - var RoomHeader = sdk.getComponent('rooms.RoomHeader'); - var MessageComposer = sdk.getComponent('rooms.MessageComposer'); - var CallView = sdk.getComponent("voip.CallView"); - var RoomSettings = sdk.getComponent("rooms.RoomSettings"); - var SearchBar = sdk.getComponent("molecules.SearchBar"); - - if (!this.state.room) { - if (this.props.roomId) { - return ( -
    - -
    - ); - } else { - return ( -
    - ); - } - } - - var myUserId = MatrixClientPeg.get().credentials.userId; - if (this.state.room.currentState.members[myUserId].membership == 'invite') { - if (this.state.joining || this.state.rejecting) { - var Loader = sdk.getComponent("elements.Spinner"); - return ( -
    - -
    - ); - } else { - var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event; - // XXX: Leaving this intentionally basic for now because invites are about to change totally - var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; - var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : ""; - return ( -
    - -
    -
    {inviteEvent.user_id} has invited you to a room
    -
    - - -
    {joinErrorText}
    -
    {rejectErrorText}
    -
    -
    - ); - } - } else { - var scrollheader_classes = classNames({ - mx_RoomView_scrollheader: true, - loading: this.state.paginating - }); - - var statusBar = ( -
    - ); - - // for testing UI... - // this.state.upload = { - // uploadedBytes: 123493, - // totalBytes: 347534, - // fileName: "testing_fooble.jpg", - // } - - if (ContentMessages.getCurrentUploads().length > 0) { - var UploadBar = sdk.getComponent('statusbar/UploadBar'); - statusBar = - } else { - var typingString = this.getWhoIsTypingString(); - //typingString = "Testing typing..."; - var unreadMsgs = this.getUnreadMessagesString(); - // no conn bar trumps unread count since you can't get unread messages - // without a connection! (technically may already have some but meh) - // It also trumps the "some not sent" msg since you can't resend without - // a connection! - if (this.state.syncState === "ERROR") { - statusBar = ( -
    - /!\ -
    -
    - Connectivity to the server has been lost. -
    -
    - Sent messages will be stored until your connection has returned. -
    -
    -
    - ); - } - else if (this.state.hasUnsentMessages) { - statusBar = ( -
    - /!\ -
    -
    - Some of your messages have not been sent. -
    -
    - - Resend all now - or select individual messages to re-send. -
    -
    -
    - ); - } - // unread count trumps who is typing since the unread count is only - // set when you've scrolled up - else if (unreadMsgs) { - statusBar = ( -
    - - {unreadMsgs} -
    - ); - } - else if (typingString) { - statusBar = ( -
    -
    ...
    - {typingString} -
    - ); - } - } - - var aux = null; - if (this.state.editingRoomSettings) { - aux = ; - } - else if (this.state.uploadingRoomSettings) { - var Loader = sdk.getComponent("elements.Spinner"); - aux = ; - } - else if (this.state.searching) { - aux = ; - } - - var conferenceCallNotification = null; - if (this.state.displayConfCallNotification) { - var supportedText; - if (!MatrixClientPeg.get().supportsVoip()) { - supportedText = " (unsupported)"; - } - conferenceCallNotification = ( -
    - Ongoing conference call {supportedText} -
    - ); - } - - var fileDropTarget = null; - if (this.state.draggingFile) { - fileDropTarget =
    -
    - Drop File Here
    - Drop File Here -
    -
    ; - } - - var messageComposer; - if (!this.state.searchResults) { - messageComposer = - - } - - return ( -
    - -
    - - { conferenceCallNotification } - { aux } -
    - -
    - { fileDropTarget } -
      -
    1. -
    2. - {this.getEventTiles()} -
    -
    -
    -
    -
    -
    - { this.state.searchResults ? null : statusBar } -
    -
    - { messageComposer } -
    - ); - } - }, -});