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/README.md b/README.md index c834e01379..529e7cdf0f 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,10 @@ about them: 2. `cd matrix-react-sdk` 3. `git checkout develop` 4. `npm install` -5. `npm start` (to start the dev rebuilder) -6. `cd ../vector-web` -7. Link the react sdk package into the example: +5. `npm run build` +6. `npm start` (to start the dev rebuilder) +7. `cd ../vector-web` +8. Link the react sdk package into the example: `npm link path/to/your/react/sdk` Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk` @@ -53,6 +54,34 @@ about "Cannot resolve module 'source-map-loader'" due to shortcomings in webpack Deployment ========== -Just run `npm build` and then mount the `vector` directory on your webserver to +Just run `npm run build` and then mount the `vector` directory on your webserver to actually serve up the app, which is entirely static content. +Enabling encryption +=================== + +End-to-end encryption in Vector and Matrix is not yet considered ready for +day-to-day use; it is experimental and should be considered only as a +proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview +of the current progress. + +To build a version of vector with support for end-to-end encryption, install +the olm module with `npm i https://matrix.org/packages/npm/olm/olm-0.1.0.tgz` +before running `npm start`. The olm library will be detected and used if +available. + +To enable encryption for a room, type + +``` +/encrypt on +``` + +in the message bar in that room. Vector will then generate a set of keys, and +encrypt all outgoing messages in that room. (Note that other people in that +room will send messages in the clear unless they also `/encrypt on`.) + +Note that historical encrypted messages cannot currently be decoded - history +is therefore lost when the page is reloaded. + +There is currently no visual indication of whether encryption is enabled for a +room, or whether a particular message was encrypted. diff --git a/package.json b/package.json index fb7558ad5a..88df783386 100644 --- a/package.json +++ b/package.json @@ -9,46 +9,51 @@ }, "license": "Apache-2.0", "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: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": "^9.0.0", "linkifyjs": "^2.0.0-beta.4", - "matrix-js-sdk": "^0.3.0", - "matrix-react-sdk": "^0.0.2", + "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", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^0.14.2", "react-dnd": "^2.0.2", "react-dnd-html5-backend": "^2.0.0", "react-dom": "^0.14.2", - "react-gemini-scrollbar": "^2.0.1", - "sanitize-html": "^1.0.0" + "react-gemini-scrollbar": "^2.0.1" }, "devDependencies": { "babel": "^5.8.23", "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/Avatar.js b/src/Avatar.js deleted file mode 100644 index afc5e9dd6d..0000000000 --- a/src/Avatar.js +++ /dev/null @@ -1,53 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -module.exports = { - avatarUrlForMember: function(member, width, height, resizeMethod) { - var url = member.getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - width, - height, - resizeMethod - ); - if (!url) { - // member can be null here currently since on invites, the JS SDK - // does not have enough info to build a RoomMember object for - // the inviter. - url = this.defaultAvatarUrlForString(member ? member.userId : ''); - } - return url; - }, - - defaultAvatarUrlForString: function(s) { - var total = 0; - for (var i = 0; i < s.length; ++i) { - total += s.charCodeAt(i); - } - switch (total % 3) { - case 0: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9QjNbxSKP4eagAFnTseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAABSwCRWJw31gAAAAASUVORK5CYII="; - case 1: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9chOaxgCP4eagAFk9seHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAtKMCks/JG8MAAAAASUVORK5CYII="; - case 2: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9YzNayQCP4eagADldseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAyiACeHwPiu4AAAAASUVORK5CYII="; - } - } -} - diff --git a/src/ContextualMenu.js b/src/ContextualMenu.js deleted file mode 100644 index 3327aa9486..0000000000 --- a/src/ContextualMenu.js +++ /dev/null @@ -1,82 +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'); - -// Shamelessly ripped off Modal.js. There's probably a better way -// of doing reusable widgets like dialog boxes & menus where we go and -// pass in a custom control as the actual body. - -module.exports = { - ContextualMenuContainerId: "mx_ContextualMenu_Container", - - getOrCreateContainer: function() { - var container = document.getElementById(this.ContextualMenuContainerId); - - if (!container) { - container = document.createElement("div"); - container.id = this.ContextualMenuContainerId; - document.body.appendChild(container); - } - - return container; - }, - - createMenu: function (Element, props) { - var self = this; - - var closeMenu = function() { - React.unmountComponentAtNode(self.getOrCreateContainer()); - - if (props && props.onFinished) props.onFinished.apply(null, arguments); - }; - - var position = { - top: props.top - 20, - }; - - var chevron = null; - if (props.left) { - chevron = - position.left = props.left + 8; - } else { - chevron = - position.right = props.right + 8; - } - - var className = 'mx_ContextualMenu_wrapper'; - - // FIXME: If a menu uses getDefaultProps it clobbers the onFinished - // property set here so you can't close the menu from a button click! - var menu = ( -
-
- {chevron} - -
-
-
- ); - - ReactDOM.render(menu, this.getOrCreateContainer()); - - return {close: closeMenu}; - }, -}; diff --git a/src/DateUtils.js b/src/DateUtils.js deleted file mode 100644 index fe363586ab..0000000000 --- a/src/DateUtils.js +++ /dev/null @@ -1,45 +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 days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; -var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - -module.exports = { - formatDate: function(date) { - // date.toLocaleTimeString is completely system dependent. - // just go 24h for now - function pad(n) { - return (n < 10 ? '0' : '') + n; - } - - var now = new Date(); - if (date.toDateString() === now.toDateString()) { - return pad(date.getHours()) + ':' + pad(date.getMinutes()); - } - else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) { - return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); - } - else if (now.getFullYear() === date.getFullYear()) { - return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); - } - else { - return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); - } - } -} - diff --git a/src/Resend.js b/src/Resend.js deleted file mode 100644 index 5d7d7815ce..0000000000 --- a/src/Resend.js +++ /dev/null @@ -1,40 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var dis = require('matrix-react-sdk/lib/dispatcher'); - -module.exports = { - resend: function(event) { - MatrixClientPeg.get().resendEvent( - event, MatrixClientPeg.get().getRoom(event.getRoomId()) - ).done(function() { - dis.dispatch({ - action: 'message_sent', - event: event - }); - }, function() { - dis.dispatch({ - action: 'message_send_failed', - event: event - }); - }); - dis.dispatch({ - action: 'message_resend_started', - event: event - }); - }, -}; \ No newline at end of file diff --git a/src/modules/VectorConferenceHandler.js b/src/VectorConferenceHandler.js similarity index 93% rename from src/modules/VectorConferenceHandler.js rename to src/VectorConferenceHandler.js index 637e34f943..7628e0f5e1 100644 --- a/src/modules/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -85,15 +85,15 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { }; /** - * Check if this room member is in fact a conference bot. - * @param {RoomMember} The room member to check + * Check if this user ID is in fact a conference bot. + * @param {string} userId The user ID to check. * @return {boolean} True if it is a conference bot. */ -module.exports.isConferenceUser = function(roomMember) { - if (roomMember.userId.indexOf("@" + USER_PREFIX) !== 0) { +module.exports.isConferenceUser = function(userId) { + if (userId.indexOf("@" + USER_PREFIX) !== 0) { return false; } - var base64part = roomMember.userId.split(":")[0].substring(1 + USER_PREFIX.length); + var base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length); if (base64part) { var decoded = new Buffer(base64part, "base64").toString(); // ! $STUFF : $STUFF diff --git a/src/component-index.js b/src/component-index.js new file mode 100644 index 0000000000..e6ff997e5a --- /dev/null +++ b/src/component-index.js @@ -0,0 +1,47 @@ +/* +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.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.login.CustomServerDialog'] = require('./components/views/login/VectorCustomServerDialog'); +module.exports.components['views.login.LoginFooter'] = require('./components/views/login/VectorLoginFooter'); +module.exports.components['views.login.LoginHeader'] = require('./components/views/login/VectorLoginHeader'); +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'); diff --git a/src/skins/vector/views/molecules/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js similarity index 92% rename from src/skins/vector/views/molecules/BottomLeftMenu.js rename to src/components/structures/BottomLeftMenu.js index 2020d29d84..f942efd56b 100644 --- a/src/skins/vector/views/molecules/BottomLeftMenu.js +++ b/src/components/structures/BottomLeftMenu.js @@ -40,13 +40,13 @@ module.exports = React.createClass({ return
{name}
} else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); + var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); return ; } }, render: function() { - var BottomLeftMenuTile = sdk.getComponent('molecules.BottomLeftMenuTile'); + var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile'); return (
diff --git a/src/skins/vector/views/pages/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js similarity index 100% rename from src/skins/vector/views/pages/CompatibilityPage.js rename to src/components/structures/CompatibilityPage.js diff --git a/src/skins/vector/views/organisms/LeftPanel.js b/src/components/structures/LeftPanel.js similarity index 81% rename from src/skins/vector/views/organisms/LeftPanel.js rename to src/components/structures/LeftPanel.js index 96d48e0e15..aaab8084d4 100644 --- a/src/skins/vector/views/organisms/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -22,6 +22,7 @@ var HTML5Backend = require('react-dnd-html5-backend'); var sdk = require('matrix-react-sdk') var dis = require('matrix-react-sdk/lib/dispatcher'); +var VectorConferenceHandler = require('../../VectorConferenceHandler'); var CallHandler = require("matrix-react-sdk/lib/CallHandler"); var LeftPanel = React.createClass({ @@ -84,9 +85,9 @@ var LeftPanel = React.createClass({ }, render: function() { - var RoomList = sdk.getComponent('organisms.RoomList'); - var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu'); - var IncomingCallBox = sdk.getComponent('molecules.voip.IncomingCallBox'); + var RoomList = sdk.getComponent('rooms.RoomList'); + var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu'); + var IncomingCallBox = sdk.getComponent('voip.IncomingCallBox'); var collapseButton; var classes = "mx_LeftPanel"; @@ -100,8 +101,12 @@ var LeftPanel = React.createClass({ var callPreview; if (this.state.showCallElement) { - var CallView = sdk.getComponent('molecules.voip.CallView'); - callPreview = + var CallView = sdk.getComponent('voip.CallView'); + callPreview = ( + + ); } return ( @@ -109,7 +114,10 @@ var LeftPanel = React.createClass({ { collapseButton } { callPreview } - + ); diff --git a/src/skins/vector/views/organisms/RightPanel.js b/src/components/structures/RightPanel.js similarity index 90% rename from src/skins/vector/views/organisms/RightPanel.js rename to src/components/structures/RightPanel.js index feebcfeba5..f4f0a8f332 100644 --- a/src/skins/vector/views/organisms/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -68,6 +68,11 @@ module.exports = React.createClass({ if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) { this.forceUpdate(); } + else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId && + member.userId === this.state.member.userId) { + // refresh the member info (e.g. new power level) + this.forceUpdate(); + } }, onAction: function(payload) { @@ -94,7 +99,7 @@ module.exports = React.createClass({ }, render: function() { - var MemberList = sdk.getComponent('organisms.MemberList'); + var MemberList = sdk.getComponent('rooms.MemberList'); var buttonGroup; var panel; @@ -122,12 +127,12 @@ module.exports = React.createClass({ buttonGroup =
- Members + Members { membersBadge } { membersHighlight }
- Files + Files { filesHighlight }
; @@ -137,7 +142,7 @@ module.exports = React.createClass({ panel = } else if(this.state.phase == this.Phase.MemberInfo) { - var MemberInfo = sdk.getComponent('molecules.MemberInfo'); + var MemberInfo = sdk.getComponent('rooms.MemberInfo'); panel = } } diff --git a/src/skins/vector/views/organisms/RoomDirectory.js b/src/components/structures/RoomDirectory.js similarity index 94% rename from src/skins/vector/views/organisms/RoomDirectory.js rename to src/components/structures/RoomDirectory.js index 28135bfeb4..c1a29779ca 100644 --- a/src/skins/vector/views/organisms/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -40,7 +40,7 @@ module.exports = React.createClass({ if (err) { self.setState({ loading: false }); console.error("Failed to get publicRooms: %s", JSON.stringify(err)); - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to get public room list", description: err.message @@ -67,7 +67,7 @@ module.exports = React.createClass({ }); }, function(err) { console.error("Failed to join room: %s", JSON.stringify(err)); - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to join room", description: err.message @@ -119,7 +119,7 @@ module.exports = React.createClass({ render: function() { if (this.state.loading) { - var Loader = sdk.getComponent("atoms.Spinner"); + var Loader = sdk.getComponent("elements.Spinner"); return (
@@ -127,7 +127,7 @@ module.exports = React.createClass({ ); } - var RoomHeader = sdk.getComponent('molecules.RoomHeader'); + var RoomHeader = sdk.getComponent('rooms.RoomHeader'); return (
diff --git a/src/skins/vector/views/organisms/RoomSubList.js b/src/components/structures/RoomSubList.js similarity index 92% rename from src/skins/vector/views/organisms/RoomSubList.js rename to src/components/structures/RoomSubList.js index 35210b499a..f7efb4254f 100644 --- a/src/skins/vector/views/organisms/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -20,6 +20,7 @@ var React = require('react'); var DropTarget = require('react-dnd').DropTarget; var sdk = require('matrix-react-sdk') var dis = require('matrix-react-sdk/lib/dispatcher'); +var UnreadStatus = require('matrix-react-sdk/lib/UnreadStatus'); // turn this on for drop & drag console debugging galore var debug = false; @@ -88,10 +89,20 @@ var RoomSubList = React.createClass({ }, tsOfNewestEvent: function(room) { - if (room.timeline.length) { - return room.timeline[room.timeline.length - 1].getTs(); + for (var i = room.timeline.length - 1; i >= 0; --i) { + var ev = room.timeline[i]; + // logic copied from RoomList.js for when we do/don't highlight + if (UnreadStatus.eventTriggersUnreadCount(ev)) { + return ev.getTs(); + } } - else { + + // we might only have events that don't trigger the unread indicator, + // in which case use the oldest event even if normally it wouldn't count. + // This is better than just assuming the last event was forever ago. + if (room.timeline.length) { + return room.timeline[0].getTs(); + } else { return Number.MAX_SAFE_INTEGER; } }, @@ -215,7 +226,7 @@ var RoomSubList = React.createClass({ makeRoomTiles: function() { var self = this; - var RoomTile = sdk.getComponent("molecules.RoomTile"); + var RoomTile = sdk.getComponent("rooms.RoomTile"); return this.state.sortedList.map(function(room) { var selected = room.roomId == self.props.selectedRoom; // XXX: is it evil to pass in self as a prop to RoomTile? @@ -235,7 +246,7 @@ var RoomSubList = React.createClass({ render: function() { var connectDropTarget = this.props.connectDropTarget; - var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget'); + var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); var label = this.props.collapsed ? null : this.props.label; @@ -265,7 +276,7 @@ var RoomSubList = React.createClass({ return connectDropTarget(

{ this.props.collapsed ? '' : this.props.label } - +

{ subList }
diff --git a/src/skins/vector/views/organisms/ViewSource.js b/src/components/structures/ViewSource.js similarity index 100% rename from src/skins/vector/views/organisms/ViewSource.js rename to src/components/structures/ViewSource.js diff --git a/src/skins/vector/views/atoms/ImageView.js b/src/components/views/elements/ImageView.js similarity index 91% rename from src/skins/vector/views/atoms/ImageView.js rename to src/components/views/elements/ImageView.js index a842f7c855..741524be86 100644 --- a/src/skins/vector/views/atoms/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -20,7 +20,7 @@ var React = require('react'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var DateUtils = require('../../../../DateUtils'); +var DateUtils = require('matrix-react-sdk/lib/DateUtils'); var filesize = require('filesize'); module.exports = React.createClass({ @@ -30,7 +30,8 @@ module.exports = React.createClass({ onFinished: React.PropTypes.func.isRequired }, - // XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class somehow, surely... + // XXX: keyboard shortcuts for managing dialogs should be done by the modal + // dialog base class somehow, surely... componentDidMount: function() { document.addEventListener("keydown", this.onKeyDown); }, @@ -54,7 +55,7 @@ module.exports = React.createClass({ ).done(function() { if (self.props.onFinished) self.props.onFinished(); }, function(e) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // display error message stating you couldn't delete this. var code = e.errcode || e.statusCode; Modal.createDialog(ErrorDialog, { @@ -104,6 +105,11 @@ module.exports = React.createClass({ res = ", " + style.width + "x" + style.height + "px"; } + var size; + if (this.props.mxEvent.getContent().info && this.props.mxEvent.getContent().info.size) { + size = filesize(this.props.mxEvent.getContent().info.size); + } + return (
@@ -123,7 +129,7 @@ module.exports = React.createClass({
Download this file
- ({ filesize(this.props.mxEvent.getContent().info.size) }{ res }) + { size } { res }
diff --git a/src/skins/vector/views/atoms/Spinner.js b/src/components/views/elements/Spinner.js similarity index 100% rename from src/skins/vector/views/atoms/Spinner.js rename to src/components/views/elements/Spinner.js diff --git a/src/skins/vector/views/molecules/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js similarity index 83% rename from src/skins/vector/views/molecules/MatrixToolbar.js rename to src/components/views/globals/MatrixToolbar.js index 5b613f563d..7b953c4a84 100644 --- a/src/skins/vector/views/molecules/MatrixToolbar.js +++ b/src/components/views/globals/MatrixToolbar.js @@ -17,30 +17,28 @@ limitations under the License. 'use strict'; var React = require('react'); - +var Notifier = require("matrix-react-sdk/lib/Notifier"); var sdk = require('matrix-react-sdk') module.exports = React.createClass({ displayName: 'MatrixToolbar', hideToolbar: function() { - var Notifier = sdk.getComponent('organisms.Notifier'); Notifier.setToolbarHidden(true); }, onClick: function() { - var Notifier = sdk.getComponent('organisms.Notifier'); Notifier.setEnabled(true); }, render: function() { return (
- /!\ + /!\
You are not receiving desktop notifications. Enable them now
-
+
); } diff --git a/src/skins/vector/views/organisms/ErrorDialog.js b/src/components/views/login/VectorCustomServerDialog.js similarity index 53% rename from src/skins/vector/views/organisms/ErrorDialog.js rename to src/components/views/login/VectorCustomServerDialog.js index 992ea05054..22c188cc7a 100644 --- a/src/skins/vector/views/organisms/ErrorDialog.js +++ b/src/components/views/login/VectorCustomServerDialog.js @@ -14,38 +14,37 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - -/* - * Usage: - * Modal.createDialog(ErrorDialog, { - * title: "some text", (default: "Error") - * description: "some more text", - * button: "Button Text", - * onClose: someFunction, - * focus: true|false (default: true) - * }); - */ - -var React = require('react'); -var ErrorDialogController = require('matrix-react-sdk/lib/controllers/organisms/ErrorDialog') +var React = require("react"); module.exports = React.createClass({ - displayName: 'ErrorDialog', - mixins: [ErrorDialogController], + displayName: 'VectorCustomServerDialog', + statics: { + replaces: 'CustomServerDialog', + }, render: function() { return (
- {this.props.title} + Custom Server Options
- {this.props.description} + + 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/views/molecules/EventAsTextTile.js b/src/components/views/login/VectorLoginFooter.js similarity index 60% rename from src/skins/vector/views/molecules/EventAsTextTile.js rename to src/components/views/login/VectorLoginFooter.js index ec644a4ec4..2b2f1ae827 100644 --- a/src/skins/vector/views/molecules/EventAsTextTile.js +++ b/src/components/views/login/VectorLoginFooter.js @@ -18,26 +18,20 @@ limitations under the License. var React = require('react'); -var TextForEvent = require('matrix-react-sdk/lib/TextForEvent'); - module.exports = React.createClass({ - displayName: 'EventAsTextTile', - + displayName: 'VectorLoginFooter', statics: { - needsSenderProfile: function() { - return false; - } + replaces: 'LoginFooter', }, render: function() { - var text = TextForEvent.textForEvent(this.props.mxEvent); - if (text == null || text.length == 0) return null; - return ( -
- {TextForEvent.textForEvent(this.props.mxEvent)} +
+ blog  ·   + twitter  ·   + github  ·   + powered by Matrix
); - }, + } }); - diff --git a/src/skins/vector/views/atoms/voip/VideoFeed.js b/src/components/views/login/VectorLoginHeader.js similarity index 75% rename from src/skins/vector/views/atoms/voip/VideoFeed.js rename to src/components/views/login/VectorLoginHeader.js index 9cf28d1ba4..61b04267fc 100644 --- a/src/skins/vector/views/atoms/voip/VideoFeed.js +++ b/src/components/views/login/VectorLoginHeader.js @@ -19,13 +19,16 @@ limitations under the License. var React = require('react'); module.exports = React.createClass({ - displayName: 'VideoFeed', + displayName: 'VectorLoginHeader', + statics: { + replaces: 'LoginHeader', + }, render: function() { return ( - +
+ vector +
); - }, + } }); - diff --git a/src/skins/vector/views/molecules/DateSeparator.js b/src/components/views/messages/DateSeparator.js similarity index 100% rename from src/skins/vector/views/molecules/DateSeparator.js rename to src/components/views/messages/DateSeparator.js diff --git a/src/skins/vector/views/atoms/MessageTimestamp.js b/src/components/views/messages/MessageTimestamp.js similarity index 93% rename from src/skins/vector/views/atoms/MessageTimestamp.js rename to src/components/views/messages/MessageTimestamp.js index 5795e55657..b4b7546e50 100644 --- a/src/skins/vector/views/atoms/MessageTimestamp.js +++ b/src/components/views/messages/MessageTimestamp.js @@ -17,7 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); -var DateUtils = require('../../../../DateUtils'); +var DateUtils = require('matrix-react-sdk/lib/DateUtils'); module.exports = React.createClass({ displayName: 'MessageTimestamp', diff --git a/src/skins/vector/views/molecules/MEmoteTile.js b/src/components/views/messages/SenderProfile.js similarity index 72% rename from src/skins/vector/views/molecules/MEmoteTile.js rename to src/components/views/messages/SenderProfile.js index de2d93650b..ef0173d975 100644 --- a/src/skins/vector/views/molecules/MEmoteTile.js +++ b/src/components/views/messages/SenderProfile.js @@ -18,19 +18,20 @@ limitations under the License. var React = require('react'); -var MEmoteTileController = require('matrix-react-sdk/lib/controllers/molecules/MEmoteTile') - module.exports = React.createClass({ - displayName: 'MEmoteTile', - mixins: [MEmoteTileController], + displayName: 'SenderProfile', render: function() { var mxEvent = this.props.mxEvent; - var content = mxEvent.getContent(); var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); + + var msgtype = mxEvent.getContent().msgtype; + if (msgtype && msgtype == 'm.emote') { + name = ''; // emote message must include the name so don't duplicate it + } return ( - - * {name} {content.body} + + {name} { this.props.aux } ); }, diff --git a/src/skins/vector/views/molecules/BottomLeftMenuTile.js b/src/components/views/rooms/BottomLeftMenuTile.js similarity index 95% rename from src/skins/vector/views/molecules/BottomLeftMenuTile.js rename to src/components/views/rooms/BottomLeftMenuTile.js index 8c28058d10..2535490fea 100644 --- a/src/skins/vector/views/molecules/BottomLeftMenuTile.js +++ b/src/components/views/rooms/BottomLeftMenuTile.js @@ -41,7 +41,7 @@ module.exports = React.createClass({ label =
{ this.props.label }
; } else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); + var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); label = ; } diff --git a/src/skins/vector/views/molecules/MessageContextMenu.js b/src/components/views/rooms/MessageContextMenu.js similarity index 76% rename from src/skins/vector/views/molecules/MessageContextMenu.js rename to src/components/views/rooms/MessageContextMenu.js index b36d4ccbbf..4950cd8825 100644 --- a/src/skins/vector/views/molecules/MessageContextMenu.js +++ b/src/components/views/rooms/MessageContextMenu.js @@ -22,7 +22,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var dis = require('matrix-react-sdk/lib/dispatcher'); var sdk = require('matrix-react-sdk') var Modal = require('matrix-react-sdk/lib/Modal'); -var Resend = require("../../../../Resend"); +var Resend = require("matrix-react-sdk/lib/Resend"); module.exports = React.createClass({ displayName: 'MessageContextMenu', @@ -33,7 +33,7 @@ module.exports = React.createClass({ }, onViewSourceClick: function() { - var ViewSource = sdk.getComponent('organisms.ViewSource'); + var ViewSource = sdk.getComponent('structures.ViewSource'); Modal.createDialog(ViewSource, { mxEvent: this.props.mxEvent }); @@ -46,7 +46,7 @@ module.exports = React.createClass({ ).done(function() { // message should disappear by itself }, function(e) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // display error message stating you couldn't delete this. var code = e.errcode || e.statusCode; Modal.createDialog(ErrorDialog, { @@ -57,25 +57,42 @@ module.exports = React.createClass({ if (this.props.onFinished) this.props.onFinished(); }, + onCancelSendClick: function() { + Resend.removeFromQueue(this.props.mxEvent); + if (this.props.onFinished) this.props.onFinished(); + }, + render: function() { + var eventStatus = this.props.mxEvent.status; var resendButton; var viewSourceButton; var redactButton; + var cancelButton; - if (this.props.mxEvent.status == 'not_sent') { + if (eventStatus === 'not_sent') { resendButton = (
Resend
); } - else { + + if (!eventStatus) { // sent redactButton = (
Redact
); } + + if (eventStatus === "queued" || eventStatus === "not_sent") { + cancelButton = ( +
+ Cancel Sending +
+ ); + } + viewSourceButton = (
View Source @@ -86,6 +103,7 @@ module.exports = React.createClass({
{resendButton} {redactButton} + {cancelButton} {viewSourceButton}
); diff --git a/src/skins/vector/views/molecules/RoomTile.js b/src/components/views/rooms/RoomDNDView.js similarity index 64% rename from src/skins/vector/views/molecules/RoomTile.js rename to src/components/views/rooms/RoomDNDView.js index 31dead455c..9b01629df2 100644 --- a/src/skins/vector/views/molecules/RoomTile.js +++ b/src/components/views/rooms/RoomDNDView.js @@ -19,13 +19,11 @@ limitations under the License. var React = require('react'); var DragSource = require('react-dnd').DragSource; var DropTarget = require('react-dnd').DropTarget; -var classNames = require('classnames'); - -var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile') +var dis = require("matrix-react-sdk/lib/dispatcher"); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -var sdk = require('matrix-react-sdk') +var sdk = require('matrix-react-sdk'); +var RoomTile = require('matrix-react-sdk/lib/components/views/rooms/RoomTile'); /** * Specifies the drag source contract. @@ -69,7 +67,7 @@ var roomTileSource = { if (monitor.didDrop() && item.targetList.props.editable) { // if we moved lists, remove the old tag - if (item.targetList !== item.originalList) { + if (item.targetList !== item.originalList && item.originalList.props.tagName) { // commented out attempts to set a spinner on our target component as component is actually // the original source component being dragged, not our target. To fix we just need to // move all of this to endDrop in the target instead. FIXME later. @@ -78,7 +76,7 @@ var roomTileSource = { MatrixClientPeg.get().deleteRoomTag(item.room.roomId, item.originalList.props.tagName).finally(function() { //component.state.set({ spinner: component.state.spinner-- }); }).fail(function(err) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to remove tag " + item.originalList.props.tagName + " from room", description: err.toString() @@ -97,7 +95,7 @@ var roomTileSource = { MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() { //component.state.set({ spinner: component.state.spinner-- }); }).fail(function(err) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to add tag " + item.targetList.props.tagName + " to room", description: err.toString() @@ -183,117 +181,6 @@ var roomTileTarget = { }, }; -var RoomTile = React.createClass({ - displayName: 'RoomTile', - mixins: [RoomTileController], - - propTypes: { - connectDragSource: React.PropTypes.func.isRequired, - connectDropTarget: React.PropTypes.func.isRequired, - isDragging: React.PropTypes.bool.isRequired, - room: React.PropTypes.object.isRequired, - collapsed: React.PropTypes.bool.isRequired, - selected: React.PropTypes.bool.isRequired, - unread: React.PropTypes.bool.isRequired, - highlight: React.PropTypes.bool.isRequired, - isInvite: React.PropTypes.bool.isRequired, - roomSubList: React.PropTypes.object.isRequired, - }, - - getInitialState: function() { - return( { hover : false }); - }, - - onMouseEnter: function() { - this.setState( { hover : true }); - }, - - onMouseLeave: function() { - this.setState( { hover : false }); - }, - - render: function() { - // if (this.props.clientOffset) { - // //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y); - // } - -/* - if (this.props.room._dragging) { - var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget"); - return ; - } -*/ - - var myUserId = MatrixClientPeg.get().credentials.userId; - var me = this.props.room.currentState.members[myUserId]; - var classes = classNames({ - 'mx_RoomTile': true, - 'mx_RoomTile_selected': this.props.selected, - 'mx_RoomTile_unread': this.props.unread, - 'mx_RoomTile_highlight': this.props.highlight, - 'mx_RoomTile_invited': (me && me.membership == 'invite'), - }); - - var name; - if (this.props.isInvite) { - name = this.props.room.getMember(myUserId).events.member.getSender(); - } - else { - // XXX: We should never display raw room IDs, but sometimes the room name js sdk gives is undefined - name = this.props.room.name || this.props.room.roomId; - } - - name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon - var badge; - if (this.props.highlight) { - badge =
; - } - /* - if (this.props.highlight) { - badge =
!
; - } - else if (this.props.unread) { - badge =
1
; - } - var nameCell; - if (badge) { - nameCell =
{name}
{badge}
; - } - else { - nameCell =
{name}
; - } - */ - - var label; - if (!this.props.collapsed) { - var className = 'mx_RoomTile_name' + (this.props.isInvite ? ' mx_RoomTile_invite' : ''); - label =
{name}
; - } - else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); - label = ; - } - - var RoomAvatar = sdk.getComponent('atoms.RoomAvatar'); - - // These props are injected by React DnD, - // as defined by your `collect` function above: - var isDragging = this.props.isDragging; - var connectDragSource = this.props.connectDragSource; - var connectDropTarget = this.props.connectDropTarget; - - return connectDragSource(connectDropTarget( -
-
- - { badge } -
- { label } -
- )); - } -}); - // Export the wrapped version, inlining the 'collect' functions // to more closely resemble the ES7 module.exports = @@ -313,4 +200,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'; diff --git a/src/skins/vector/views/molecules/RoomDropTarget.js b/src/components/views/rooms/RoomDropTarget.js similarity index 100% rename from src/skins/vector/views/molecules/RoomDropTarget.js rename to src/components/views/rooms/RoomDropTarget.js diff --git a/src/skins/vector/views/molecules/RoomTooltip.js b/src/components/views/rooms/RoomTooltip.js similarity index 100% rename from src/skins/vector/views/molecules/RoomTooltip.js rename to src/components/views/rooms/RoomTooltip.js diff --git a/src/skins/vector/views/molecules/SearchBar.js b/src/components/views/rooms/SearchBar.js similarity index 57% rename from src/skins/vector/views/molecules/SearchBar.js rename to src/components/views/rooms/SearchBar.js index 585b9a6d4a..e66f123609 100644 --- a/src/skins/vector/views/molecules/SearchBar.js +++ b/src/components/views/rooms/SearchBar.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var sdk = require('matrix-react-sdk'); +var classNames = require('classnames'); module.exports = React.createClass({ displayName: 'SearchBar', @@ -39,17 +40,29 @@ module.exports = React.createClass({ onSearchChange: function(e) { if (e.keyCode === 13) { // on enter... - this.props.onSearch(this.refs.search_term.value, this.state.scope); + this.onSearch(); } + if (e.keyCode === 27) { // escape... + this.props.onCancelClick(); + } + }, + + onSearch: function() { + this.props.onSearch(this.refs.search_term.value, this.state.scope); }, render: function() { + var searchButtonClasses = classNames({ mx_SearchBar_searchButton : true, mx_SearchBar_searching: this.props.searchInProgress }); + var thisRoomClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'Room' }); + var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' }); + return (
-
This Room
-
All Rooms
- +
Search
+
This Room
+
All Rooms
+
); } diff --git a/src/controllers/molecules/voip/CallView.js b/src/controllers/molecules/voip/CallView.js deleted file mode 100644 index ab71214882..0000000000 --- a/src/controllers/molecules/voip/CallView.js +++ /dev/null @@ -1,97 +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 dis = require("matrix-react-sdk/lib/dispatcher"); -var CallHandler = require("matrix-react-sdk/lib/CallHandler"); -var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); - -var VectorConferenceHandler = require('../../../modules/VectorConferenceHandler'); - -/* - * State vars: - * this.state.call = MatrixCall|null - * - * Props: - * this.props.room = Room (JS SDK) - * - * Internal state: - * this._trackedRoom = (either from props.room or programatically set) - */ - -module.exports = { - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - this._trackedRoom = null; - if (this.props.room) { - this._trackedRoom = this.props.room; - this.showCall(this._trackedRoom.roomId); - } - else { - var call = CallHandler.getAnyActiveCall(); - if (call) { - console.log( - "Global CallView is now tracking active call in room %s", - call.roomId - ); - this._trackedRoom = MatrixClientPeg.get().getRoom(call.roomId); - this.showCall(call.roomId); - } - } - }, - - componentWillUnmount: function() { - dis.unregister(this.dispatcherRef); - }, - - onAction: function(payload) { - // don't filter out payloads for room IDs other than props.room because - // we may be interested in the conf 1:1 room - if (payload.action !== 'call_state' || !payload.room_id) { - return; - } - this.showCall(payload.room_id); - }, - - showCall: function(roomId) { - var call = ( - CallHandler.getCallForRoom(roomId) || - VectorConferenceHandler.getConferenceCallForRoom(roomId) - ); - if (call) { - call.setLocalVideoElement(this.getVideoView().getLocalVideoElement()); - call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement()); - // give a separate element for audio stream playback - both for voice calls - // and for the voice stream of screen captures - call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement()); - } - if (call && call.type === "video" && call.state !== 'ended') { - // if this call is a conf call, don't display local video as the - // conference will have us in it - this.getVideoView().getLocalVideoElement().style.display = ( - call.confUserId ? "none" : "initial" - ); - this.getVideoView().getRemoteVideoElement().style.display = "initial"; - } - else { - this.getVideoView().getLocalVideoElement().style.display = "none"; - this.getVideoView().getRemoteVideoElement().style.display = "none"; - dis.dispatch({action: 'video_fullscreen', fullscreen: false}); - } - } -}; - diff --git a/src/controllers/organisms/RoomList.js b/src/controllers/organisms/RoomList.js deleted file mode 100644 index 37d4a4e4a2..0000000000 --- a/src/controllers/organisms/RoomList.js +++ /dev/null @@ -1,201 +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 RoomListSorter = require("matrix-react-sdk/lib/RoomListSorter"); -var dis = require("matrix-react-sdk/lib/dispatcher"); - -var sdk = require('matrix-react-sdk'); -var VectorConferenceHandler = require("../../modules/VectorConferenceHandler"); - -var HIDE_CONFERENCE_CHANS = true; - -module.exports = { - getInitialState: function() { - return { - activityMap: null, - lists: {}, - } - }, - - componentWillMount: function() { - var cli = MatrixClientPeg.get(); - cli.on("Room", this.onRoom); - cli.on("Room.timeline", this.onRoomTimeline); - cli.on("Room.name", this.onRoomName); - cli.on("Room.tags", this.onRoomTags); - cli.on("RoomState.events", this.onRoomStateEvents); - cli.on("RoomMember.name", this.onRoomMemberName); - - var s = this.getRoomLists(); - s.activityMap = {}; - this.setState(s); - }, - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - }, - - onAction: function(payload) { - switch (payload.action) { - case 'view_tooltip': - this.tooltip = payload.tooltip; - this._repositionTooltip(); - if (this.tooltip) this.tooltip.style.display = 'block'; - break - } - }, - - componentWillUnmount: function() { - dis.unregister(this.dispatcherRef); - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("Room", this.onRoom); - MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); - MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); - MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); - } - }, - - componentWillReceiveProps: function(newProps) { - this.state.activityMap[newProps.selectedRoom] = undefined; - this.setState({ - activityMap: this.state.activityMap - }); - }, - - onRoom: function(room) { - this.refreshRoomList(); - }, - - onRoomTimeline: function(ev, room, toStartOfTimeline) { - if (toStartOfTimeline) return; - - var newState = this.getRoomLists(); - if ( - room.roomId != this.props.selectedRoom && - ev.getSender() != MatrixClientPeg.get().credentials.userId) - { - var hl = 1; - - var actions = MatrixClientPeg.get().getPushActionsForEvent(ev); - if (actions && actions.tweaks && actions.tweaks.highlight) { - hl = 2; - } - // obviously this won't deep copy but this shouldn't be necessary - var amap = this.state.activityMap; - amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl); - - newState.activityMap = amap; - } - this.setState(newState); - }, - - onRoomName: function(room) { - this.refreshRoomList(); - }, - - onRoomTags: function(event, room) { - this.refreshRoomList(); - }, - - onRoomStateEvents: function(ev, state) { - setTimeout(this.refreshRoomList, 0); - }, - - onRoomMemberName: function(ev, member) { - setTimeout(this.refreshRoomList, 0); - }, - - refreshRoomList: function() { - // TODO: rather than bluntly regenerating and re-sorting everything - // every time we see any kind of room change from the JS SDK - // we could do incremental updates on our copy of the state - // based on the room which has actually changed. This would stop - // us re-rendering all the sublists every time anything changes anywhere - // in the state of the client. - this.setState(this.getRoomLists()); - }, - - getRoomLists: function() { - var s = { lists: {} }; - - s.lists["m.invite"] = []; - s.lists["m.favourite"] = []; - s.lists["m.recent"] = []; - s.lists["m.lowpriority"] = []; - s.lists["m.archived"] = []; - - MatrixClientPeg.get().getRooms().forEach(function(room) { - var me = room.getMember(MatrixClientPeg.get().credentials.userId); - - if (me && me.membership == "invite") { - s.lists["m.invite"].push(room); - } - else { - var shouldShowRoom = ( - me && (me.membership == "join") - ); - - // hiding conf rooms only ever toggles shouldShowRoom to false - if (shouldShowRoom && HIDE_CONFERENCE_CHANS) { - // we want to hide the 1:1 conf<->user room and not the group chat - var joinedMembers = room.getJoinedMembers(); - if (joinedMembers.length === 2) { - var otherMember = joinedMembers.filter(function(m) { - return m.userId !== me.userId - })[0]; - if (VectorConferenceHandler.isConferenceUser(otherMember)) { - // console.log("Hiding conference 1:1 room %s", room.roomId); - shouldShowRoom = false; - } - } - } - - if (shouldShowRoom) { - var tagNames = Object.keys(room.tags); - if (tagNames.length) { - for (var i = 0; i < tagNames.length; i++) { - var tagName = tagNames[i]; - s.lists[tagName] = s.lists[tagName] || []; - s.lists[tagNames[i]].push(room); - } - } - else { - s.lists["m.recent"].push(room); - } - } - } - }); - - //console.log("calculated new roomLists; m.recent = " + s.lists["m.recent"]); - - // we actually apply the sorting to this when receiving the prop in RoomSubLists. - - return s; - }, - - _repositionTooltip: function(e) { - if (this.tooltip && this.tooltip.parentElement) { - var scroll = ReactDOM.findDOMNode(this); - this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.children[2].scrollTop) + "px"; - } - }, -}; diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js deleted file mode 100644 index e603198a72..0000000000 --- a/src/controllers/organisms/RoomView.js +++ /dev/null @@ -1,628 +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("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("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; - } - }, - - _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 - }); - } - }, - - 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.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; - 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) { - this.setState({ - upload: { - fileName: file.name, - uploadedBytes: 0, - totalBytes: file.size - } - }); - var self = this; - ContentMessages.sendContentToRoom( - file, this.props.roomId, MatrixClientPeg.get() - ).progress(function(ev) { - //console.log("Upload: "+ev.loaded+" / "+ev.total); - 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") { // FIXME: should be enum - 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", - event_context: { - before_limit: 1, - after_limit: 1, - } - } - } - } - }).then(function(data) { - self.setState({ - searchTerm: term, - searchResults: data, - }); - }, 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 ret = []; - var count = 0; - - var EventTile = sdk.getComponent('molecules.EventTile'); - - if (this.state.searchResults) { - // XXX: this dance is foul, due to the results API not returning sorted results - var results = this.state.searchResults.search_categories.room_events.results; - var eventIds = Object.keys(results); - // XXX: todo: merge overlapping results somehow? - // XXX: why doesn't searching on name work? - var resultList = eventIds.map(function(key) { return results[key]; }); // .sort(function(a, b) { b.rank - a.rank }); - for (var i = 0; i < resultList.length; 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, - }); - } - } -}; diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js deleted file mode 100644 index 5f88d59059..0000000000 --- a/src/controllers/templates/Register.js +++ /dev/null @@ -1,58 +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 extend = require('matrix-react-sdk/lib/extend'); -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var BaseRegisterController = require('matrix-react-sdk/lib/controllers/templates/Register.js'); - -var RegisterController = {}; -extend(RegisterController, BaseRegisterController); - -RegisterController.onRegistered = function(user_id, access_token) { - MatrixClientPeg.replaceUsingAccessToken( - this.state.hs_url, this.state.is_url, user_id, access_token - ); - - this.setState({ - step: 'profile', - busy: true - }); - - var self = this; - var cli = MatrixClientPeg.get(); - cli.getProfileInfo(cli.credentials.userId).done(function(result) { - self.setState({ - avatarUrl: result.avatar_url, - busy: false - }); - }, - function(err) { - console.err(err); - self.setState({ - busy: false - }); - }); -}; - -RegisterController.onAccountReady = function() { - if (this.props.onLoggedIn) { - this.props.onLoggedIn(); - } -}; - -module.exports = RegisterController; diff --git a/src/skins/vector/header b/src/header similarity index 100% rename from src/skins/vector/header rename to src/header 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..bff271a354 --- /dev/null +++ b/src/skins/vector/css/MOVE_ME/UploadBar.css @@ -0,0 +1,44 @@ +.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; + opacity: 0.6; + cursor: pointer; + z-index: 1; +} + +.mx_UploadBar_uploadBytes { + float: right; + margin-top: 5px; + margin-right: 30px; + color: #76cfa6; +} diff --git a/src/skins/vector/css/atoms/ImageView.css b/src/skins/vector/css/atoms/ImageView.css index 22ef343bee..9dd34f804e 100644 --- a/src/skins/vector/css/atoms/ImageView.css +++ b/src/skins/vector/css/atoms/ImageView.css @@ -87,13 +87,13 @@ limitations under the License. } .mx_ImageView_name { - font-size: 20px; + font-size: 18px; margin-bottom: 6px; pointer-events: all; } .mx_ImageView_metadata { - font-size: 16px; + font-size: 15px; opacity: 0.5; pointer-events: all; } @@ -105,13 +105,13 @@ limitations under the License. margin-bottom: 6px; border-radius: 5px; background-color: #454545; - font-size: 16px; + font-size: 14px; padding: 9px; border: 1px solid #fff; } .mx_ImageView_size { - font-size: 12px; + font-size: 11px; } .mx_ImageView_link { @@ -121,7 +121,7 @@ limitations under the License. .mx_ImageView_button { pointer-events: all; - font-size: 16px; + font-size: 15px; opacity: 0.5; margin-top: 18px; cursor: pointer; diff --git a/src/skins/vector/css/atoms/MemberAvatar.css b/src/skins/vector/css/atoms/MemberAvatar.css index 34ef139364..3da56172d1 100644 --- a/src/skins/vector/css/atoms/MemberAvatar.css +++ b/src/skins/vector/css/atoms/MemberAvatar.css @@ -20,11 +20,14 @@ limitations under the License. .mx_MemberAvatar_initial { position: absolute; + z-index: 1; color: #fff; text-align: center; speak: none; + pointer-events: none; } .mx_MemberAvatar_image { - border-radius: 20px; + border-radius: 20px; + vertical-align: top; } diff --git a/src/skins/vector/css/atoms/RoomAvatar.css b/src/skins/vector/css/atoms/RoomAvatar.css index 01425190ee..f005b25176 100644 --- a/src/skins/vector/css/atoms/RoomAvatar.css +++ b/src/skins/vector/css/atoms/RoomAvatar.css @@ -15,6 +15,7 @@ limitations under the License. */ .mx_RoomAvatar { + vertical-align: middle; } .mx_RoomAvatar_initial { @@ -23,4 +24,5 @@ limitations under the License. text-align: center; font-weight: normal ! important; speak: none; + pointer-events: none; } \ No newline at end of file diff --git a/src/skins/vector/css/common.css b/src/skins/vector/css/common.css index 121fbca7a4..2b6e02ed37 100644 --- a/src/skins/vector/css/common.css +++ b/src/skins/vector/css/common.css @@ -22,8 +22,13 @@ html { } body { - font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; - font-size: 16px; + /* Open Sans lacks combining diacritics, so these will fall through + to the next font. Helevetica's diacritics however do not combine + nicely with Open Sans (on OSX, at least) and result in a huge + horizontal mess. Arial empirically gets it right, hence prioritising + Arial here. */ + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; + font-size: 15px; color: #454545; border: 0px; margin: 0px; @@ -36,7 +41,7 @@ div.error { h2 { color: #454545; font-weight: 400; - font-size: 20px; + font-size: 18px; margin-top: 16px; margin-bottom: 16px; } @@ -47,6 +52,12 @@ a:visited { color: #76cfa6; } +input[type=text]:focus, textarea:focus, .mx_RoomSettings textarea:focus { + border: 1px solid #76CFA6; + outline: none; + box-shadow: none; +} + /* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48. Stop the scrollbar view from pushing out the container's overall sizing, which causes flexbox to adapt to the new size and cause the view to keep growing. @@ -99,19 +110,9 @@ a:visited { margin: 0 auto; } -.mx_Dialog_background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #000; - opacity: 0.2; - z-index: 4000; -} - .mx_Dialog_wrapper { position: fixed; + z-index: 4000; top: 0; left: 0; width: 100%; @@ -134,12 +135,22 @@ a:visited { text-align: center; z-index: 4010; font-weight: 300; - font-size: 16px; + font-size: 15px; position: relative; border-radius: 8px; max-width: 80%; } +.mx_Dialog_background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #000; + opacity: 0.2; +} + .mx_Dialog_lightbox .mx_Dialog_background { opacity: 0.85; } @@ -167,7 +178,7 @@ a:visited { height: 36px; border-radius: 36px; font-weight: 400; - font-size: 16px; + font-size: 15px; color: #fff; background-color: #76cfa6; margin-left: 8px; @@ -182,6 +193,6 @@ a:visited { padding: 12px; border-bottom: 1px solid #a4a4a4; font-weight: bold; - font-size: 20px; + font-size: 18px; line-height: 1.4; } 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/molecules/EventAsTextTile.css b/src/skins/vector/css/molecules/EventAsTextTile.css index d18fdc809c..da95352296 100644 --- a/src/skins/vector/css/molecules/EventAsTextTile.css +++ b/src/skins/vector/css/molecules/EventAsTextTile.css @@ -16,4 +16,5 @@ limitations under the License. .mx_EventAsTextTile { opacity: 0.5; + overflow-y: hidden; } diff --git a/src/skins/vector/css/molecules/EventTile.css b/src/skins/vector/css/molecules/EventTile.css index d99bd4e1b5..e3956bdb2b 100644 --- a/src/skins/vector/css/molecules/EventTile.css +++ b/src/skins/vector/css/molecules/EventTile.css @@ -25,8 +25,10 @@ limitations under the License. padding-left: 18px; padding-right: 12px; margin-left: -73px; - margin-top: -4px; + margin-top: -2px; float: left; + position: relative; + top: 0px; } .mx_EventTile_avatar img { @@ -41,15 +43,15 @@ limitations under the License. .mx_EventTile .mx_SenderProfile { color: #454545; opacity: 0.5; - font-size: 14px; + font-size: 13px; margin-bottom: 4px; display: block; + overflow-y: hidden; } .mx_EventTile .mx_MessageTimestamp { color: #acacac; - font-size: 12px; - float: right; + font-size: 11px; } .mx_EventTile_line { @@ -64,8 +66,39 @@ limitations under the License. .mx_MessageTile_content { display: block; margin-right: 100px; + overflow-y: hidden; } +/* Various markdown overrides */ + +.mx_MessageTile_content .markdown-body { + font-family: inherit ! important; + white-space: normal ! important; + line-height: inherit ! important; + color: inherit; + font-size: 15px; +} + +.mx_MessageTile_content .markdown-body h1, +.mx_MessageTile_content .markdown-body h2, +.mx_MessageTile_content .markdown-body h3, +.mx_MessageTile_content .markdown-body h4, +.mx_MessageTile_content .markdown-body h5, +.mx_MessageTile_content .markdown-body h6 +{ + font-family: inherit ! important; +} + +.mx_MessageTile_content .markdown-body a { + color: #76cfa6; +} + +.mx_MessageTile_content .markdown-body .hljs { + display: inherit ! important; +} + +/* end of overrides */ + .mx_MessageTile_searchHighlight { background-color: #76cfa6; color: #fff; @@ -78,10 +111,12 @@ limitations under the License. } .mx_EventTile_notSent { - color: #ddd; + color: #f44; } -.mx_EventTile_highlight { +.mx_EventTile_highlight, +.mx_EventTile_highlight .markdown-body + { color: #FF0064; } @@ -91,10 +126,18 @@ limitations under the License. .mx_EventTile_msgOption { float: right; + text-align: right; + z-index: 1; + position: relative; + width: 90px; + margin-right: 10px; + margin-top: -6px; } .mx_MessageTimestamp { + display: block; visibility: hidden; + text-align: right; } .mx_EventTile_last .mx_MessageTimestamp { @@ -107,9 +150,8 @@ limitations under the License. .mx_EventTile_editButton { position: absolute; - right: 1px; - top: 15px; - visibility: hidden; + display: inline-block; + visibility: hidden; } .mx_EventTile:hover .mx_EventTile_editButton { @@ -123,3 +165,21 @@ limitations under the License. .mx_EventTile.menu .mx_MessageTimestamp { visibility: visible; } + +.mx_EventTile_readAvatars { + position: relative; + display: inline-block; + width: 14px; + height: 14px; +} + +.mx_EventTile_readAvatars .mx_MemberAvatar { + position: absolute; + display: inline-block; +} + +.mx_EventTile_readAvatarRemainder { + color: #acacac; + font-size: 11px; + position: absolute; +} diff --git a/src/skins/vector/css/molecules/MNoticeTile.css b/src/skins/vector/css/molecules/MNoticeTile.css index 0a0db62ea6..9fe5376a9e 100644 --- a/src/skins/vector/css/molecules/MNoticeTile.css +++ b/src/skins/vector/css/molecules/MNoticeTile.css @@ -15,5 +15,6 @@ limitations under the License. */ .mx_MNoticeTile { + white-space: pre-wrap; opacity: 0.6; } diff --git a/src/skins/vector/css/molecules/MemberInfo.css b/src/skins/vector/css/molecules/MemberInfo.css index 6471a86cb6..1c4ab3856c 100644 --- a/src/skins/vector/css/molecules/MemberInfo.css +++ b/src/skins/vector/css/molecules/MemberInfo.css @@ -37,8 +37,10 @@ limitations under the License. } .mx_MemberInfo_profileField { - opacity: 0.6; - font-size: 14px; + font-color: #999999; + font-size: 13px; + position: relative; + background-color: #fff; } .mx_MemberInfo_buttons { @@ -49,7 +51,7 @@ limitations under the License. cursor: pointer; width: 100px; text-align: center; - font-size: 12px; + font-size: 11px; background-color: #888; color: #fff; font-weight: bold; diff --git a/src/skins/vector/css/molecules/MemberTile.css b/src/skins/vector/css/molecules/MemberTile.css index cfeaeaee0c..874710d9de 100644 --- a/src/skins/vector/css/molecules/MemberTile.css +++ b/src/skins/vector/css/molecules/MemberTile.css @@ -25,8 +25,8 @@ limitations under the License. display: table-cell; padding-left: 3px; padding-right: 12px; - padding-top: 2px; - padding-bottom: 0px; + padding-top: 4px; + padding-bottom: 4px; vertical-align: middle; width: 36px; height: 36px; @@ -55,13 +55,13 @@ limitations under the License. } .mx_MemberTile_userId { - font-size: 14px; + font-size: 13px; overflow: hidden; text-overflow: ellipsis; } .mx_MemberTile_presence { - font-size: 12px; + font-size: 11px; opacity: 0.5; } @@ -98,10 +98,6 @@ limitations under the License. opacity: 0.25; } -.mx_MemberTile_zalgo { - font-family: Helvetica, Arial, Sans-Serif; -} - .mx_MemberTile:hover .mx_MessageTimestamp { display: block; } diff --git a/src/skins/vector/css/molecules/MessageComposer.css b/src/skins/vector/css/molecules/MessageComposer.css index fbbeef6455..4d5668518d 100644 --- a/src/skins/vector/css/molecules/MessageComposer.css +++ b/src/skins/vector/css/molecules/MessageComposer.css @@ -59,7 +59,7 @@ limitations under the License. box-shadow: none; /* needed for FF */ - font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; } /* hack for FF as vertical alignment of custom placeholder text is broken */ @@ -72,7 +72,9 @@ limitations under the License. } .mx_MessageComposer_upload, -.mx_MessageComposer_call { +.mx_MessageComposer_hangup, +.mx_MessageComposer_voicecall, +.mx_MessageComposer_videocall { display: table-cell; vertical-align: middle; padding-left: 10px; @@ -80,7 +82,12 @@ limitations under the License. cursor: pointer; } -.mx_MessageComposer_call { +.mx_MessageComposer_videocall { + padding-right: 10px; + padding-top: 4px; +} + +.mx_MessageComposer_voicecall { padding-right: 10px; padding-top: 4px; } diff --git a/src/skins/vector/css/molecules/RoomDropTarget.css b/src/skins/vector/css/molecules/RoomDropTarget.css index 4eea49e155..2e655c7376 100644 --- a/src/skins/vector/css/molecules/RoomDropTarget.css +++ b/src/skins/vector/css/molecules/RoomDropTarget.css @@ -15,7 +15,7 @@ limitations under the License. */ .mx_RoomDropTarget { - font-size: 14px; + font-size: 13px; margin-left: 10px; margin-right: 15px; padding-top: 5px; diff --git a/src/skins/vector/css/molecules/RoomHeader.css b/src/skins/vector/css/molecules/RoomHeader.css index e033d735cd..b1469e3557 100644 --- a/src/skins/vector/css/molecules/RoomHeader.css +++ b/src/skins/vector/css/molecules/RoomHeader.css @@ -23,6 +23,9 @@ limitations under the License. height: 83px; border-bottom: 1px solid #eeeeee; + -webkit-align-items: center; + align-items: center; + display: -webkit-box; display: -moz-box; display: -ms-flexbox; @@ -31,8 +34,6 @@ limitations under the License. } .mx_RoomHeader_leftRow { - height: 48px; - margin-top: 18px; margin-left: -2px; -webkit-box-ordinal-group: 1; @@ -47,7 +48,6 @@ limitations under the License. .mx_RoomHeader_textButton { height: 48px; - margin-top: 18px; background-color: #76cfa6; border-radius: 48px; margin-right: 8px; @@ -72,7 +72,7 @@ limitations under the License. } .mx_RoomHeader_rightRow { - margin-top: 32px; + margin-top: 4px; background-color: #fff; -webkit-box-ordinal-group: 3; @@ -84,14 +84,14 @@ limitations under the License. .mx_RoomHeader_info { display: table-cell; - height: 48px; + /* height: 48px; */ vertical-align: middle; } .mx_RoomHeader_simpleHeader { line-height: 83px; color: #454545; - font-size: 24px; + font-size: 22px; font-weight: bold; overflow: hidden; margin-left: 63px; @@ -110,11 +110,13 @@ limitations under the License. cursor: pointer; vertical-align: middle; height: 28px; + overflow: hidden; color: #454545; font-weight: bold; - font-size: 24px; + font-size: 22px; padding-left: 19px; padding-right: 16px; + /* why isn't text-overflow working? */ text-overflow: ellipsis; } @@ -122,6 +124,13 @@ limitations under the License. display: inline-block; } +.mx_RoomHeader_searchStatus { + display: inline-block; + font-weight: normal; + overflow-y: hidden; + opacity: 0.6; +} + .mx_RoomHeader_settingsButton { display: inline-block; visibility: hidden; @@ -130,14 +139,24 @@ limitations under the License. left: 4px; } -.mx_RoomHeader_name:hover { +.mx_RoomHeader_leftRow:hover .mx_RoomHeader_name { color: #76cfa6; } -.mx_RoomHeader_name:hover .mx_RoomHeader_settingsButton { +.mx_RoomHeader_leftRow:hover .mx_RoomHeader_settingsButton { visibility: visible; } +.mx_RoomHeader_leaveButton { + visibility: hidden; + margin-top: -1px; +} + +.mx_RoomHeader_wrapper:hover .mx_RoomHeader_leaveButton { + visibility: visible; +} + + .mx_RoomHeader_nameEditing { padding-left: 8px; padding-right: 16px; @@ -149,7 +168,7 @@ limitations under the License. width: 260px; border: 1px solid #c7c7c7; font-weight: 300; - font-size: 14px; + font-size: 13px; padding: 9px; } @@ -160,7 +179,7 @@ limitations under the License. .mx_RoomHeader_topic { vertical-align: bottom; float: left; - max-height: 38px; + max-height: 42px; color: #454545; font-weight: 300; padding-left: 19px; diff --git a/src/skins/vector/css/molecules/RoomSettings.css b/src/skins/vector/css/molecules/RoomSettings.css index a669c5b2d1..dabdd55f09 100644 --- a/src/skins/vector/css/molecules/RoomSettings.css +++ b/src/skins/vector/css/molecules/RoomSettings.css @@ -39,7 +39,7 @@ limitations under the License. border-radius: 3px; border: 1px solid #c7c7c7; font-weight: 300; - font-size: 14px; + font-size: 13px; padding: 9px; margin-top: 6px; } @@ -59,7 +59,7 @@ limitations under the License. height: 36px; border-radius: 36px; font-weight: 400; - font-size: 16px; + font-size: 15px; color: #fff; background-color: #76cfa6; width: auto; diff --git a/src/skins/vector/css/molecules/RoomTile.css b/src/skins/vector/css/molecules/RoomTile.css index 37d2e1b62e..ef907d25a1 100644 --- a/src/skins/vector/css/molecules/RoomTile.css +++ b/src/skins/vector/css/molecules/RoomTile.css @@ -18,19 +18,19 @@ limitations under the License. cursor: pointer; /* This fixes wrapping of long room names, but breaks drag & drop previews */ /* display: table-row; */ - font-size: 14px; + font-size: 13px; } .mx_RoomTile_avatar { display: table-cell; padding-right: 8px; - padding-top: 4px; - padding-bottom: 2px; + padding-top: 6px; + padding-bottom: 6px; padding-left: 18px; - vertical-align: middle; width: 24px; height: 24px; position: relative; + vertical-align: middle; } .mx_RoomTile_avatar img { @@ -48,7 +48,8 @@ limitations under the License. } .mx_RoomTile_invite { - color: rgba(69, 69, 69, 0.5); +/* color: rgba(69, 69, 69, 0.5); +*/ } .collapsed .mx_RoomTile_name { @@ -112,6 +113,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; diff --git a/src/skins/vector/css/molecules/SearchBar.css b/src/skins/vector/css/molecules/SearchBar.css index b116674bc5..3698c852a9 100644 --- a/src/skins/vector/css/molecules/SearchBar.css +++ b/src/skins/vector/css/molecules/SearchBar.css @@ -21,24 +21,43 @@ limitations under the License. align-items: center; } -.mx_SearchBar input { +.mx_SearchBar_input { display: inline block; - border-radius: 3px; + border-radius: 3px 0px 0px 3px; border: 1px solid #f0f0f0; - font-size: 16px; + font-size: 15px; padding: 9px; padding-left: 11px; - margin-right: 17px; width: auto; flex: 1 1 0; } +.mx_SearchBar_searchButton { + cursor: pointer; + margin-right: 10px; + width: 37px; + height: 37px; + border-radius: 0px 3px 3px 0px; + background-color: #76CFA6; +} + +@keyframes pulsate { + 0% { opacity: 1.0; } + 50% { opacity: 0.25; } + 100% { opacity: 1.0; } +} + +.mx_SearchBar_searching img { + animation: pulsate 0.75s ease-out; + animation-iteration-count: infinite; +} + .mx_SearchBar_button { display: inline; border: 0px; border-radius: 36px; font-weight: 400; - font-size: 16px; + font-size: 15px; color: #fff; background-color: #76cfa6; width: auto; diff --git a/src/skins/vector/css/molecules/SenderProfile.css b/src/skins/vector/css/molecules/SenderProfile.css index 45db913e68..fd88ee27f7 100644 --- a/src/skins/vector/css/molecules/SenderProfile.css +++ b/src/skins/vector/css/molecules/SenderProfile.css @@ -13,8 +13,3 @@ 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. */ - -.mx_SenderProfile_zalgo { - font-family: Helvetica, Arial, Sans-Serif; - display: table-row ! important; -} diff --git a/src/skins/vector/css/molecules/ServerConfig.css b/src/skins/vector/css/molecules/ServerConfig.css index db0572c841..58bdcfdf94 100644 --- a/src/skins/vector/css/molecules/ServerConfig.css +++ b/src/skins/vector/css/molecules/ServerConfig.css @@ -25,7 +25,7 @@ limitations under the License. .mx_ServerConfig_help:link { opacity: 0.8; - font-size: 14px; + font-size: 13px; font-weight: 300; color: #4a4a4a; } \ No newline at end of file diff --git a/src/skins/vector/css/organisms/CreateRoom.css b/src/skins/vector/css/organisms/CreateRoom.css index 578c79e6ea..feb8bbff36 100644 --- a/src/skins/vector/css/organisms/CreateRoom.css +++ b/src/skins/vector/css/organisms/CreateRoom.css @@ -26,7 +26,7 @@ limitations under the License. border-radius: 3px; border: 1px solid #c7c7c7; font-weight: 300; - font-size: 14px; + font-size: 13px; padding: 9px; margin-top: 6px; } diff --git a/src/skins/vector/css/organisms/LeftPanel.css b/src/skins/vector/css/organisms/LeftPanel.css index 37de0f0e54..7451d1677f 100644 --- a/src/skins/vector/css/organisms/LeftPanel.css +++ b/src/skins/vector/css/organisms/LeftPanel.css @@ -69,11 +69,12 @@ limitations under the License. } .mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options { - margin-top: 17px; + margin-top: 15px; width: 100%; } .mx_LeftPanel .mx_BottomLeftMenu img { border-radius: 0px; background-color: transparent; + vertical-align: middle; } \ No newline at end of file diff --git a/src/skins/vector/css/organisms/MemberList.css b/src/skins/vector/css/organisms/MemberList.css index df699e6490..ce936d2c85 100644 --- a/src/skins/vector/css/organisms/MemberList.css +++ b/src/skins/vector/css/organisms/MemberList.css @@ -45,13 +45,13 @@ limitations under the License. } .mx_MemberList_invite { - font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; border-radius: 3px; border: 1px solid #f0f0f0; padding: 9px; color: #454545; margin-left: 3px; - font-size: 16px; + font-size: 15px; margin-bottom: 8px; width: 180px; } @@ -69,7 +69,7 @@ limitations under the License. text-transform: uppercase; color: #3d3b39; font-weight: 600; - font-size: 14px; + font-size: 13px; padding-left: 3px; padding-right: 12px; margin-top: 8px; diff --git a/src/skins/vector/css/organisms/RightPanel.css b/src/skins/vector/css/organisms/RightPanel.css index bf473a4489..645a626314 100644 --- a/src/skins/vector/css/organisms/RightPanel.css +++ b/src/skins/vector/css/organisms/RightPanel.css @@ -66,7 +66,7 @@ limitations under the License. .mx_RightPanel_headerButton_badge { position: absolute; - top: 5px; + top: 4px; left: 28px; font-size: 12px; background-color: #76cfa6; @@ -75,7 +75,7 @@ limitations under the License. border-radius: 20px; padding-left: 4px; padding-right: 4px; - padding-top: 2px; + padding-top: 0px; } .mx_RightPanel .mx_MemberList, diff --git a/src/skins/vector/css/organisms/RoomDirectory.css b/src/skins/vector/css/organisms/RoomDirectory.css index f53f055657..61fcfa6e3b 100644 --- a/src/skins/vector/css/organisms/RoomDirectory.css +++ b/src/skins/vector/css/organisms/RoomDirectory.css @@ -50,7 +50,7 @@ limitations under the License. border-radius: 3px; border: 1px solid #c7c7c7; font-weight: 300; - font-size: 14px; + font-size: 13px; padding: 9px; margin-top: 12px; margin-bottom: 12px; @@ -70,7 +70,7 @@ limitations under the License. .mx_RoomDirectory_table th { font-weight: 400; - font-size: 12px; + font-size: 11px; } .mx_RoomDirectory_table tbody { @@ -79,7 +79,6 @@ limitations under the License. .mx_RoomDirectory_table td { font-weight: 300; - font-size: 16px; overflow-x: hidden; text-overflow: ellipsis; } @@ -90,7 +89,7 @@ limitations under the License. .mx_RoomDirectory_table .mx_RoomDirectory_topic { font-weight: 400; - font-size: 12px; + font-size: 11px; } .mx_RoomDirectory_table td, diff --git a/src/skins/vector/css/organisms/RoomList.css b/src/skins/vector/css/organisms/RoomList.css index 7f5e2272cd..bb81686c38 100644 --- a/src/skins/vector/css/organisms/RoomList.css +++ b/src/skins/vector/css/organisms/RoomList.css @@ -25,3 +25,10 @@ limitations under the License. padding-left: 12px; padding-right: 12px; } + +/* Evil hacky override until Chrome fixes drop and drag table cells + and we can correctly fix horizontal wrapping in the sidebar again */ +.mx_RoomList_scrollbar .gm-scroll-view { + overflow-x: hidden ! important; + overflow-y: scroll ! important; +} diff --git a/src/skins/vector/css/organisms/RoomSubList.css b/src/skins/vector/css/organisms/RoomSubList.css index 57d23a3837..b143c998c0 100644 --- a/src/skins/vector/css/organisms/RoomSubList.css +++ b/src/skins/vector/css/organisms/RoomSubList.css @@ -29,7 +29,7 @@ limitations under the License. text-transform: uppercase; color: #3d3b39; font-weight: 600; - font-size: 14px; + font-size: 13px; padding-left: 12px; padding-right: 12px; margin-top: 8px; diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css index 94fff29068..2358bc095e 100644 --- a/src/skins/vector/css/organisms/RoomView.css +++ b/src/skins/vector/css/organisms/RoomView.css @@ -170,10 +170,35 @@ limitations under the License. .mx_RoomView_statusAreaBox_line { border-top: 1px solid #eee; - margin-left: 63px; height: 1px; } +.mx_RoomView_inCall .mx_RoomView_statusAreaBox_line { + border-top: 1px hidden; +} + +.mx_RoomView_inCall .mx_MessageComposer_wrapper { + border-top: 2px hidden; +} + +.mx_RoomView_inCall .mx_RoomView_statusAreaBox { + background-color: #76CFA6; + color: #fff; + position: relative; +} + +.mx_RoomView_voipChevron { + position: absolute; + bottom: -10px; + right: 11px; +} + +.mx_RoomView_voipButton { + float: right; + margin-right: 13px; + cursor: pointer; +} + .mx_RoomView_unreadMessagesBar { color: #ff0064; cursor: pointer; @@ -186,6 +211,16 @@ limitations under the License. vertical-align: middle; } +.mx_RoomView_callBar { + margin-top: 5px; +} + +.mx_RoomView_callBar img { + padding-left: 13px; + padding-right: 30px; + vertical-align: middle; +} + .mx_RoomView_connectionLostBar { margin-top: 19px; height: 58px; @@ -204,7 +239,7 @@ limitations under the License. .mx_RoomView_connectionLostBar_desc { color: #454545; - font-size: 14px; + font-size: 13px; opacity: 0.5; } @@ -215,8 +250,8 @@ limitations under the License. } .mx_RoomView_typingBar { - margin-top: 10px; - margin-left: 63px; + margin-top: 6px; + margin-left: 65px; color: #4a4a4a; opacity: 0.5; } @@ -228,6 +263,11 @@ limitations under the License. float: left; } +.mx_RoomView_typingText { + overflow-y: hidden; + display: block; +} + .mx_RoomView .mx_MessageComposer { -webkit-box-ordinal-group: 5; -moz-box-ordinal-group: 5; @@ -241,43 +281,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/css/pages/MatrixChat.css b/src/skins/vector/css/pages/MatrixChat.css index b95f6a415c..2190e49601 100644 --- a/src/skins/vector/css/pages/MatrixChat.css +++ b/src/skins/vector/css/pages/MatrixChat.css @@ -14,6 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_MatrixChat_splash { + position: relative; + height: 100%; +} + +.mx_MatrixChat_splashButtons { + text-align: center; + width: 100%; + position: absolute; + bottom: 30px; +} + .mx_MatrixChat_wrapper { display: -webkit-box; display: -moz-box; diff --git a/src/skins/vector/css/templates/Login.css b/src/skins/vector/css/templates/Login.css index 11fba43fbc..d1b28b1e59 100644 --- a/src/skins/vector/css/templates/Login.css +++ b/src/skins/vector/css/templates/Login.css @@ -55,7 +55,7 @@ limitations under the License. border-radius: 3px; border: 1px solid #c7c7c7; font-weight: 300; - font-size: 14px; + font-size: 13px; padding: 9px; margin-bottom: 14px; } @@ -68,12 +68,12 @@ limitations under the License. height: 40px; border: 0px; background-color: #76cfa6; - font-size: 16px; + font-size: 15px; color: #fff; } .mx_Login_label { - font-size: 14px; + font-size: 13px; opacity: 0.8; } @@ -85,7 +85,7 @@ limitations under the License. display: block; text-align: center; width: 100%; - font-size: 14px; + font-size: 13px; opacity: 0.8; } @@ -97,7 +97,7 @@ limitations under the License. display: block; text-align: center; width: 100%; - font-size: 14px; + font-size: 13px; opacity: 0.8; } diff --git a/src/skins/vector/fonts/OpenSans.css b/src/skins/vector/fonts/OpenSans.css new file mode 100644 index 0000000000..05be90d52c --- /dev/null +++ b/src/skins/vector/fonts/OpenSans.css @@ -0,0 +1,12 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf) format('truetype'); +} diff --git a/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf new file mode 100644 index 0000000000..bffc5fcf61 Binary files /dev/null and b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf differ diff --git a/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf b/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf new file mode 100644 index 0000000000..89ce23b4b4 Binary files /dev/null and b/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf differ diff --git a/src/skins/vector/img/call.svg b/src/skins/vector/img/call.svg new file mode 100644 index 0000000000..f528f9a24e --- /dev/null +++ b/src/skins/vector/img/call.svg @@ -0,0 +1,17 @@ + + + + icons_video + Created with bin/sketchtool. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/cancel.svg b/src/skins/vector/img/cancel.svg new file mode 100644 index 0000000000..e32060025e --- /dev/null +++ b/src/skins/vector/img/cancel.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/files.svg b/src/skins/vector/img/files.svg new file mode 100644 index 0000000000..20aba851ea --- /dev/null +++ b/src/skins/vector/img/files.svg @@ -0,0 +1,18 @@ + + + + icons_browse_files + Created with bin/sketchtool. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/fullscreen.svg b/src/skins/vector/img/fullscreen.svg new file mode 100644 index 0000000000..e333abb6fb --- /dev/null +++ b/src/skins/vector/img/fullscreen.svg @@ -0,0 +1,23 @@ + + + + Zoom + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/hangup.svg b/src/skins/vector/img/hangup.svg new file mode 100644 index 0000000000..be038d2b30 --- /dev/null +++ b/src/skins/vector/img/hangup.svg @@ -0,0 +1,15 @@ + + + + Fill 72 + Path 98 + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/leave.svg b/src/skins/vector/img/leave.svg new file mode 100644 index 0000000000..8485607360 --- /dev/null +++ b/src/skins/vector/img/leave.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/skins/vector/img/list-close.svg b/src/skins/vector/img/list-close.svg new file mode 100644 index 0000000000..eb60864e2c --- /dev/null +++ b/src/skins/vector/img/list-close.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/list-open.svg b/src/skins/vector/img/list-open.svg new file mode 100644 index 0000000000..a682ec9051 --- /dev/null +++ b/src/skins/vector/img/list-open.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/members.svg b/src/skins/vector/img/members.svg new file mode 100644 index 0000000000..0f115966ab --- /dev/null +++ b/src/skins/vector/img/members.svg @@ -0,0 +1,14 @@ + + + + icons_people + Created with bin/sketchtool. + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/search-button.svg b/src/skins/vector/img/search-button.svg new file mode 100644 index 0000000000..f4808842ff --- /dev/null +++ b/src/skins/vector/img/search-button.svg @@ -0,0 +1,15 @@ + + + + icon_search + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/search.svg b/src/skins/vector/img/search.svg new file mode 100644 index 0000000000..bd4cd9200c --- /dev/null +++ b/src/skins/vector/img/search.svg @@ -0,0 +1,17 @@ + + + + icons_search + Created with bin/sketchtool. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/settings.svg b/src/skins/vector/img/settings.svg new file mode 100644 index 0000000000..4190c7b8de --- /dev/null +++ b/src/skins/vector/img/settings.svg @@ -0,0 +1,12 @@ + + + + icon_settings_small + Created with bin/sketchtool. + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/sound-indicator.svg b/src/skins/vector/img/sound-indicator.svg new file mode 100644 index 0000000000..9b8de53d81 --- /dev/null +++ b/src/skins/vector/img/sound-indicator.svg @@ -0,0 +1,17 @@ + + + + sound_indicator + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/tab.svg b/src/skins/vector/img/tab.svg new file mode 100644 index 0000000000..eae92dcf32 --- /dev/null +++ b/src/skins/vector/img/tab.svg @@ -0,0 +1,16 @@ + + + + icon_eol + Created with bin/sketchtool. + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/upload-big.svg b/src/skins/vector/img/upload-big.svg new file mode 100644 index 0000000000..6099c2e976 --- /dev/null +++ b/src/skins/vector/img/upload-big.svg @@ -0,0 +1,19 @@ + + + + icons_upload_drop + Created with bin/sketchtool. + + + + + + + + + + + + + + diff --git a/src/skins/vector/img/upload-original.svg b/src/skins/vector/img/upload-original.svg new file mode 100644 index 0000000000..962fc49d77 --- /dev/null +++ b/src/skins/vector/img/upload-original.svg @@ -0,0 +1,19 @@ + + + + icons_upload + Created with bin/sketchtool. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/upload.svg b/src/skins/vector/img/upload.svg new file mode 100644 index 0000000000..039014a2f3 --- /dev/null +++ b/src/skins/vector/img/upload.svg @@ -0,0 +1,19 @@ + + + + icons_upload + Created with bin/sketchtool. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/video-mute.svg b/src/skins/vector/img/video-mute.svg new file mode 100644 index 0000000000..6de60ba39b --- /dev/null +++ b/src/skins/vector/img/video-mute.svg @@ -0,0 +1,17 @@ + + + + icons_video copy + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/video-unmute.svg b/src/skins/vector/img/video-unmute.svg new file mode 100644 index 0000000000..a6c6c3b681 --- /dev/null +++ b/src/skins/vector/img/video-unmute.svg @@ -0,0 +1,18 @@ + + + + icons_video copy + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/voice-mute.svg b/src/skins/vector/img/voice-mute.svg new file mode 100644 index 0000000000..336641078e --- /dev/null +++ b/src/skins/vector/img/voice-mute.svg @@ -0,0 +1,14 @@ + + + + Audio + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/voice-unmute.svg b/src/skins/vector/img/voice-unmute.svg new file mode 100644 index 0000000000..0d7e6f429f --- /dev/null +++ b/src/skins/vector/img/voice-unmute.svg @@ -0,0 +1,15 @@ + + + + Audio + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/voice.png b/src/skins/vector/img/voice.png new file mode 100644 index 0000000000..5ba765b0f4 Binary files /dev/null and b/src/skins/vector/img/voice.png differ diff --git a/src/skins/vector/img/voice.svg b/src/skins/vector/img/voice.svg new file mode 100644 index 0000000000..ff87270ba5 --- /dev/null +++ b/src/skins/vector/img/voice.svg @@ -0,0 +1,13 @@ + + + + icon_voice + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/voip-chevron.svg b/src/skins/vector/img/voip-chevron.svg new file mode 100644 index 0000000000..5f7cbe7153 --- /dev/null +++ b/src/skins/vector/img/voip-chevron.svg @@ -0,0 +1,12 @@ + + + + Triangle 1 + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/voip-mute.png b/src/skins/vector/img/voip-mute.png new file mode 100644 index 0000000000..a16d1001e5 Binary files /dev/null and b/src/skins/vector/img/voip-mute.png differ diff --git a/src/skins/vector/img/warning.svg b/src/skins/vector/img/warning.svg new file mode 100644 index 0000000000..b9a96a88e5 --- /dev/null +++ b/src/skins/vector/img/warning.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js deleted file mode 100644 index cf279c872d..0000000000 --- a/src/skins/vector/skindex.js +++ /dev/null @@ -1,93 +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 = {}; - -skin['atoms.EditableText'] = require('./views/atoms/EditableText'); -skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton'); -skin['atoms.ImageView'] = require('./views/atoms/ImageView'); -skin['atoms.LogoutButton'] = require('./views/atoms/LogoutButton'); -skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar'); -skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp'); -skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar'); -skin['atoms.Spinner'] = require('./views/atoms/Spinner'); -skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton'); -skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets'); -skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias'); -skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed'); -skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu'); -skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile'); -skin['molecules.ChangeAvatar'] = require('./views/molecules/ChangeAvatar'); -skin['molecules.ChangeDisplayName'] = require('./views/molecules/ChangeDisplayName'); -skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword'); -skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator'); -skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile'); -skin['molecules.EventTile'] = require('./views/molecules/EventTile'); -skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile'); -skin['molecules.MFileTile'] = require('./views/molecules/MFileTile'); -skin['molecules.MImageTile'] = require('./views/molecules/MImageTile'); -skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile'); -skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile'); -skin['molecules.MTextTile'] = require('./views/molecules/MTextTile'); -skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar'); -skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo'); -skin['molecules.MemberTile'] = require('./views/molecules/MemberTile'); -skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer'); -skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu'); -skin['molecules.MessageTile'] = require('./views/molecules/MessageTile'); -skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar'); -skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate'); -skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget'); -skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader'); -skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings'); -skin['molecules.RoomTile'] = require('./views/molecules/RoomTile'); -skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip'); -skin['molecules.SearchBar'] = require('./views/molecules/SearchBar'); -skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile'); -skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig'); -skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile'); -skin['molecules.UserSelector'] = require('./views/molecules/UserSelector'); -skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView'); -skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox'); -skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView'); -skin['organisms.CasLogin'] = require('./views/organisms/CasLogin'); -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'); -skin['templates.Login'] = require('./views/templates/Login'); -skin['templates.Register'] = require('./views/templates/Register'); - -module.exports = skin; \ No newline at end of file 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/skins/vector/views/atoms/EditableText.js b/src/skins/vector/views/atoms/EditableText.js deleted file mode 100644 index 1848b029a6..0000000000 --- a/src/skins/vector/views/atoms/EditableText.js +++ /dev/null @@ -1,76 +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 EditableTextController = require('matrix-react-sdk/lib/controllers/atoms/EditableText') - -module.exports = React.createClass({ - displayName: 'EditableText', - mixins: [EditableTextController], - - onKeyUp: function(ev) { - if (ev.key == "Enter") { - this.onFinish(ev); - } else if (ev.key == "Escape") { - this.cancelEdit(); - } - }, - - onClickDiv: function() { - this.setState({ - phase: this.Phases.Edit, - }) - }, - - onFocus: function(ev) { - ev.target.setSelectionRange(0, ev.target.value.length); - }, - - onFinish: function(ev) { - if (ev.target.value) { - this.setValue(ev.target.value, ev.key === "Enter"); - } else { - this.cancelEdit(); - } - }, - - render: function() { - var editable_el; - - if (this.state.phase == this.Phases.Display) { - if (this.state.value) { - editable_el =
    {this.state.value}
    ; - } else { - editable_el =
    {this.props.label}
    ; - } - } else if (this.state.phase == this.Phases.Edit) { - editable_el = ( -
    - -
    - ); - } - - return ( -
    - {editable_el} -
    - ); - } -}); diff --git a/src/skins/vector/views/atoms/EnableNotificationsButton.js b/src/skins/vector/views/atoms/EnableNotificationsButton.js deleted file mode 100644 index edef9edc68..0000000000 --- a/src/skins/vector/views/atoms/EnableNotificationsButton.js +++ /dev/null @@ -1,38 +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 EnableNotificationsButtonController = require('matrix-react-sdk/lib/controllers/atoms/EnableNotificationsButton') - -module.exports = React.createClass({ - displayName: 'EnableNotificationsButton', - mixins: [EnableNotificationsButtonController], - - render: function() { - if (this.enabled()) { - return ( - - ); - } else { - return ( - - ); - } - } -}); diff --git a/src/skins/vector/views/atoms/LogoutButton.js b/src/skins/vector/views/atoms/LogoutButton.js deleted file mode 100644 index 619160f61c..0000000000 --- a/src/skins/vector/views/atoms/LogoutButton.js +++ /dev/null @@ -1,32 +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 LogoutButtonController = require('matrix-react-sdk/lib/controllers/atoms/LogoutButton') - -module.exports = React.createClass({ - displayName: 'LogoutButton', - mixins: [LogoutButtonController], - - render: function() { - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/MemberAvatar.js b/src/skins/vector/views/atoms/MemberAvatar.js deleted file mode 100644 index c8606cd72e..0000000000 --- a/src/skins/vector/views/atoms/MemberAvatar.js +++ /dev/null @@ -1,68 +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 Avatar = require('../../../../Avatar'); - -var MemberAvatarController = require('matrix-react-sdk/lib/controllers/atoms/MemberAvatar') - -module.exports = React.createClass({ - displayName: 'MemberAvatar', - mixins: [MemberAvatarController], - - avatarUrlForMember: function(member) { - return Avatar.avatarUrlForMember( - member, - this.props.member, - this.props.width, - this.props.height, - this.props.resizeMethod - ); - }, - - skinnedDefaultAvatarUrl: function(member, width, height, resizeMethod) { - return Avatar.defaultAvatarUrlForString(member.userId); - }, - - render: function() { - // XXX: recalculates default avatar url constantly - if (this.state.imageUrl === this.defaultAvatarUrl(this.props.member)) { - var initial; - if (this.props.member.name[0]) - initial = this.props.member.name[0].toUpperCase(); - if (initial === '@' && this.props.member.name[1]) - initial = this.props.member.name[1].toUpperCase(); - - return ( - - - - - ); - } - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/RoomAvatar.js b/src/skins/vector/views/atoms/RoomAvatar.js deleted file mode 100644 index bdd28bad59..0000000000 --- a/src/skins/vector/views/atoms/RoomAvatar.js +++ /dev/null @@ -1,75 +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 RoomAvatarController = require('matrix-react-sdk/lib/controllers/atoms/RoomAvatar') - -module.exports = React.createClass({ - displayName: 'RoomAvatar', - mixins: [RoomAvatarController], - - getUrlList: function() { - return [ - this.roomAvatarUrl(), - this.getOneToOneAvatar(), - this.getFallbackAvatar() - ]; - }, - - getFallbackAvatar: function() { - var images = [ '76cfa6', '50e2c2', 'f4c371' ]; - var total = 0; - for (var i = 0; i < this.props.room.roomId.length; ++i) { - total += this.props.room.roomId.charCodeAt(i); - } - return 'img/' + images[total % images.length] + '.png'; - }, - - render: function() { - var style = { - width: this.props.width, - height: this.props.height, - }; - - // XXX: recalculates fallback avatar constantly - if (this.state.imageUrl === this.getFallbackAvatar()) { - var initial; - if (this.props.room.name[0]) - initial = this.props.room.name[0].toUpperCase(); - if ((initial === '@' || initial === '#') && this.props.room.name[1]) - initial = this.props.room.name[1].toUpperCase(); - - return ( - - - - - ); - } - else { - return - } - - } -}); diff --git a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js b/src/skins/vector/views/atoms/create_room/CreateRoomButton.js deleted file mode 100644 index 2fc9d057a0..0000000000 --- a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js +++ /dev/null @@ -1,32 +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 CreateRoomButtonController = require('matrix-react-sdk/lib/controllers/atoms/create_room/CreateRoomButton') - -module.exports = React.createClass({ - displayName: 'CreateRoomButton', - mixins: [CreateRoomButtonController], - - render: function() { - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/create_room/Presets.js b/src/skins/vector/views/atoms/create_room/Presets.js deleted file mode 100644 index a098a7d7e8..0000000000 --- a/src/skins/vector/views/atoms/create_room/Presets.js +++ /dev/null @@ -1,40 +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 PresetsController = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets') - -module.exports = React.createClass({ - displayName: 'CreateRoomPresets', - mixins: [PresetsController], - - onValueChanged: function(ev) { - this.props.onChange(ev.target.value) - }, - - render: function() { - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/create_room/RoomAlias.js b/src/skins/vector/views/atoms/create_room/RoomAlias.js deleted file mode 100644 index 0a8cadc888..0000000000 --- a/src/skins/vector/views/atoms/create_room/RoomAlias.js +++ /dev/null @@ -1,79 +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 RoomAliasController = require('matrix-react-sdk/lib/controllers/atoms/create_room/RoomAlias') - -module.exports = React.createClass({ - displayName: 'RoomAlias', - mixins: [RoomAliasController], - - onValueChanged: function(ev) { - this.props.onChange(ev.target.value); - }, - - onFocus: function(ev) { - var target = ev.target; - var curr_val = ev.target.value; - - if (this.props.homeserver) { - if (curr_val == "") { - setTimeout(function() { - target.value = "#:" + this.props.homeserver; - target.setSelectionRange(1, 1); - }, 0); - } else { - var suffix = ":" + this.props.homeserver; - setTimeout(function() { - target.setSelectionRange( - curr_val.startsWith("#") ? 1 : 0, - curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length - ); - }, 0); - } - } - }, - - onBlur: function(ev) { - var curr_val = ev.target.value; - - if (this.props.homeserver) { - if (curr_val == "#:" + this.props.homeserver) { - ev.target.value = ""; - return; - } - - if (curr_val != "") { - var new_val = ev.target.value; - var suffix = ":" + this.props.homeserver; - if (!curr_val.startsWith("#")) new_val = "#" + new_val; - if (!curr_val.endsWith(suffix)) new_val = new_val + suffix; - ev.target.value = new_val; - } - } - }, - - render: function() { - return ( - - ); - } -}); diff --git a/src/skins/vector/views/molecules/ChangeAvatar.js b/src/skins/vector/views/molecules/ChangeAvatar.js deleted file mode 100644 index 7afac77fd5..0000000000 --- a/src/skins/vector/views/molecules/ChangeAvatar.js +++ /dev/null @@ -1,76 +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 sdk = require('matrix-react-sdk') -var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar') - -module.exports = React.createClass({ - displayName: 'ChangeAvatar', - mixins: [ChangeAvatarController], - - onFileSelected: function(ev) { - this.avatarSet = true; - this.setAvatarFromFile(ev.target.files[0]); - }, - - onError: function(error) { - this.setState({ - errorText: "Failed to upload profile picture!" - }); - }, - - render: function() { - var RoomAvatar = sdk.getComponent('atoms.RoomAvatar'); - var avatarImg; - // Having just set an avatar we just display that since it will take a little - // time to propagate through to the RoomAvatar. - if (this.props.room && !this.avatarSet) { - avatarImg = ; - } else { - var style = { - maxWidth: 320, - maxHeight: 240, - }; - avatarImg = ; - } - - switch (this.state.phase) { - case this.Phases.Display: - case this.Phases.Error: - return ( -
    -
    - {avatarImg} -
    -
    - Upload new: - - {this.state.errorText} -
    -
    - ); - case this.Phases.Uploading: - var Loader = sdk.getComponent("atoms.Spinner"); - return ( - - ); - } - } -}); diff --git a/src/skins/vector/views/molecules/ChangeDisplayName.js b/src/skins/vector/views/molecules/ChangeDisplayName.js deleted file mode 100644 index a10ba2a754..0000000000 --- a/src/skins/vector/views/molecules/ChangeDisplayName.js +++ /dev/null @@ -1,55 +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 sdk = require('matrix-react-sdk'); - -var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName"); - -module.exports = React.createClass({ - displayName: 'ChangeDisplayName', - mixins: [ChangeDisplayNameController], - - edit: function() { - this.refs.displayname_edit.edit() - }, - - onValueChanged: function(new_value, shouldSubmit) { - if (shouldSubmit) { - this.changeDisplayname(new_value); - } - }, - - render: function() { - if (this.state.busy) { - var Loader = sdk.getComponent("atoms.Spinner"); - return ( - - ); - } else if (this.state.errorString) { - return ( -
    {this.state.errorString}
    - ); - } else { - var EditableText = sdk.getComponent('atoms.EditableText'); - return ( - - ); - } - } -}); diff --git a/src/skins/vector/views/molecules/ChangePassword.js b/src/skins/vector/views/molecules/ChangePassword.js deleted file mode 100644 index b1d8f28e6f..0000000000 --- a/src/skins/vector/views/molecules/ChangePassword.js +++ /dev/null @@ -1,84 +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 ChangePasswordController = require('matrix-react-sdk/lib/controllers/molecules/ChangePassword') - -module.exports = React.createClass({ - displayName: 'ChangePassword', - mixins: [ChangePasswordController], - - onClickChange: function() { - var old_password = this.refs.old_input.value; - var new_password = this.refs.new_input.value; - var confirm_password = this.refs.confirm_input.value; - if (new_password != confirm_password) { - this.setState({ - state: this.Phases.Error, - errorString: "Passwords don't match" - }); - } else if (new_password == '' || old_password == '') { - this.setState({ - state: this.Phases.Error, - errorString: "Passwords can't be empty" - }); - } else { - this.changePassword(old_password, new_password); - } - }, - - render: function() { - switch (this.state.phase) { - case this.Phases.Edit: - case this.Phases.Error: - return ( -
    -
    -
    {this.state.errorString}
    -
    -
    -
    -
    -
    - - -
    -
    - ); - case this.Phases.Uploading: - var Loader = sdk.getComponent("atoms.Spinner"); - return ( -
    - -
    - ); - case this.Phases.Success: - return ( -
    -
    - Success! -
    -
    - -
    -
    - ) - } - } -}); diff --git a/src/skins/vector/views/molecules/EventTile.js b/src/skins/vector/views/molecules/EventTile.js deleted file mode 100644 index c5cb81951b..0000000000 --- a/src/skins/vector/views/molecules/EventTile.js +++ /dev/null @@ -1,140 +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 classNames = require("classnames"); - -var sdk = require('matrix-react-sdk') - -var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile') -var ContextualMenu = require('../../../../ContextualMenu'); - -var TextForEvent = require('matrix-react-sdk/lib/TextForEvent'); - -var eventTileTypes = { - 'm.room.message': 'molecules.MessageTile', - 'm.room.member' : 'molecules.EventAsTextTile', - 'm.call.invite' : 'molecules.EventAsTextTile', - 'm.call.answer' : 'molecules.EventAsTextTile', - 'm.call.hangup' : 'molecules.EventAsTextTile', - 'm.room.name' : 'molecules.EventAsTextTile', - 'm.room.topic' : 'molecules.EventAsTextTile', -}; - -module.exports = React.createClass({ - displayName: 'EventTile', - mixins: [EventTileController], - - statics: { - haveTileForEvent: function(e) { - if (eventTileTypes[e.getType()] == undefined) return false; - if (eventTileTypes[e.getType()] == 'molecules.EventAsTextTile') { - return TextForEvent.textForEvent(e) !== ''; - } else { - return true; - } - } - }, - - getInitialState: function() { - return {menu: false}; - }, - - onEditClicked: function(e) { - var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu'); - var buttonRect = e.target.getBoundingClientRect() - var x = buttonRect.right; - var y = buttonRect.top + (e.target.height / 2); - var self = this; - ContextualMenu.createMenu(MessageContextMenu, { - mxEvent: this.props.mxEvent, - left: x, - top: y, - onFinished: function() { - self.setState({menu: false}); - } - }); - this.setState({menu: true}); - }, - - render: function() { - var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp'); - var SenderProfile = sdk.getComponent('molecules.SenderProfile'); - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - - var content = this.props.mxEvent.getContent(); - var msgtype = content.msgtype; - - var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]); - // This shouldn't happen: the caller should check we support this type - // before trying to instantiate us - if (!EventTileType) { - throw new Error("Event type not supported"); - } - - var classes = classNames({ - mx_EventTile: true, - mx_EventTile_sending: ['sending', 'queued'].indexOf( - this.props.mxEvent.status - ) !== -1, - mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent', - mx_EventTile_highlight: this.shouldHighlight(), - mx_EventTile_continuation: this.props.continuation, - mx_EventTile_last: this.props.last, - mx_EventTile_contextual: this.props.contextual, - menu: this.state.menu, - }); - var timestamp = - var editButton = ( - - ); - - var aux = null; - if (msgtype === 'm.image') aux = "sent an image"; - else if (msgtype === 'm.video') aux = "sent a video"; - else if (msgtype === 'm.file') aux = "uploaded a file"; - - var avatar, sender; - if (!this.props.continuation) { - if (this.props.mxEvent.sender) { - avatar = ( -
    - -
    - ); - } - if (EventTileType.needsSenderProfile()) { - sender = ; - } - } - return ( -
    - { avatar } - { sender } -
    - { timestamp } - { editButton } - -
    -
    - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MFileTile.js b/src/skins/vector/views/molecules/MFileTile.js deleted file mode 100644 index 9180bd6b83..0000000000 --- a/src/skins/vector/views/molecules/MFileTile.js +++ /dev/null @@ -1,54 +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 MFileTileController = require('matrix-react-sdk/lib/controllers/molecules/MFileTile') - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -module.exports = React.createClass({ - displayName: 'MFileTile', - mixins: [MFileTileController], - - render: function() { - var content = this.props.mxEvent.getContent(); - var cli = MatrixClientPeg.get(); - - var httpUrl = cli.mxcUrlToHttp(content.url); - var text = this.presentableTextForFile(content); - - if (httpUrl) { - return ( - - - - ); - } else { - var extra = text ? ': '+text : ''; - return - Invalid file{extra} - - } - }, -}); diff --git a/src/skins/vector/views/molecules/MImageTile.js b/src/skins/vector/views/molecules/MImageTile.js deleted file mode 100644 index 2f3b7a55db..0000000000 --- a/src/skins/vector/views/molecules/MImageTile.js +++ /dev/null @@ -1,105 +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 filesize = require('filesize'); - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var Modal = require('matrix-react-sdk/lib/Modal'); -var sdk = require('matrix-react-sdk') - -module.exports = React.createClass({ - displayName: 'MImageTile', - - thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) { - if (!fullWidth || !fullHeight) { - // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even - // log this because it's spammy - return undefined; - } - if (fullWidth < thumbWidth && fullHeight < thumbHeight) { - // no scaling needs to be applied - return fullHeight; - } - var widthMulti = thumbWidth / fullWidth; - var heightMulti = thumbHeight / fullHeight; - if (widthMulti < heightMulti) { - // width is the dominant dimension so scaling will be fixed on that - return Math.floor(widthMulti * fullHeight); - } - else { - // height is the dominant dimension so scaling will be fixed on that - return Math.floor(heightMulti * fullHeight); - } - }, - - onClick: function(ev) { - if (ev.button == 0 && !ev.metaKey) { - ev.preventDefault(); - var content = this.props.mxEvent.getContent(); - var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(content.url); - var ImageView = sdk.getComponent("atoms.ImageView"); - Modal.createDialog(ImageView, { - src: httpUrl, - width: content.info.w, - height: content.info.h, - mxEvent: this.props.mxEvent, - }, "mx_Dialog_lightbox"); - } - }, - - render: function() { - var content = this.props.mxEvent.getContent(); - var cli = MatrixClientPeg.get(); - - var thumbHeight = null; - if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 480, 360); - - var imgStyle = {}; - if (thumbHeight) imgStyle['height'] = thumbHeight; - - var thumbUrl = cli.mxcUrlToHttp(content.url, 480, 360); - if (thumbUrl) { - return ( - - - {content.body} - - - - ); - } else if (content.body) { - return ( - - Image '{content.body}' cannot be displayed. - - ); - } else { - return ( - - This image cannot be displayed. - - ); - } - }, -}); diff --git a/src/skins/vector/views/molecules/MNoticeTile.js b/src/skins/vector/views/molecules/MNoticeTile.js deleted file mode 100644 index a0cedb1ddd..0000000000 --- a/src/skins/vector/views/molecules/MNoticeTile.js +++ /dev/null @@ -1,89 +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 sanitizeHtml = require('sanitize-html'); - -var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile') - -var allowedAttributes = sanitizeHtml.defaults.allowedAttributes; -allowedAttributes['font'] = ['color']; -var sanitizeHtmlParams = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]), - allowedAttributes: allowedAttributes, -}; - -module.exports = React.createClass({ - displayName: 'MNoticeTile', - mixins: [MNoticeTileController], - - // FIXME: this entire class is copy-pasted from MTextTile :( - render: function() { - var content = this.props.mxEvent.getContent(); - var originalBody = content.body; - var body; - - if (this.props.searchTerm) { - var lastOffset = 0; - var bodyList = []; - var k = 0; - var offset; - - // XXX: rather than searching for the search term in the body, - // we should be looking at the match delimiters returned by the FTS engine - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams); - while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) { - // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means - // hooking into the sanitizer parser rather than treating it as a string. Otherwise - // the act of highlighting a or whatever will break the HTML badly. - bodyList.push(); - bodyList.push(); - lastOffset = offset + safeSearchTerm.length; - } - bodyList.push(); - } - else { - while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) { - bodyList.push({ originalBody.substring(lastOffset, offset) }); - bodyList.push({ this.props.searchTerm }); - lastOffset = offset + this.props.searchTerm.length; - } - bodyList.push({ originalBody.substring(lastOffset) }); - } - body = bodyList; - } - else { - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - body = ; - } - else { - body = originalBody; - } - } - - return ( - - { body } - - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MRoomMemberTile.js b/src/skins/vector/views/molecules/MRoomMemberTile.js deleted file mode 100644 index 0048306d39..0000000000 --- a/src/skins/vector/views/molecules/MRoomMemberTile.js +++ /dev/null @@ -1,52 +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 sdk = require('matrix-react-sdk') -var TextForEvent = require('matrix-react-sdk/lib/TextForEvent'); - -module.exports = React.createClass({ - displayName: 'MRoomMemberTile', - - getMemberEventText: function() { - return TextForEvent.textForEvent(this.props.mxEvent); - }, - - render: function() { - // XXX: for now, just cheekily borrow the css from message tile... - var timestamp = this.props.last ? : null; - var text = this.getMemberEventText(); - if (!text) return
    ; - var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp'); - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( -
    -
    - -
    - { timestamp } - - - { text } - -
    - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MTextTile.js b/src/skins/vector/views/molecules/MTextTile.js deleted file mode 100644 index 12bafa37ba..0000000000 --- a/src/skins/vector/views/molecules/MTextTile.js +++ /dev/null @@ -1,89 +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 sanitizeHtml = require('sanitize-html'); - -var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile') - -var allowedAttributes = sanitizeHtml.defaults.allowedAttributes; -allowedAttributes['font'] = ['color']; -var sanitizeHtmlParams = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]), - allowedAttributes: allowedAttributes, -}; - -module.exports = React.createClass({ - displayName: 'MTextTile', - mixins: [MTextTileController], - - // FIXME: this entire class is copy-pasted from MTextTile :( - render: function() { - var content = this.props.mxEvent.getContent(); - var originalBody = content.body; - var body; - - if (this.props.searchTerm) { - var lastOffset = 0; - var bodyList = []; - var k = 0; - var offset; - - // XXX: rather than searching for the search term in the body, - // we should be looking at the match delimiters returned by the FTS engine - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams); - while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) { - // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means - // hooking into the sanitizer parser rather than treating it as a string. Otherwise - // the act of highlighting a or whatever will break the HTML badly. - bodyList.push(); - bodyList.push(); - lastOffset = offset + safeSearchTerm.length; - } - bodyList.push(); - } - else { - while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) { - bodyList.push({ originalBody.substring(lastOffset, offset) }); - bodyList.push({ this.props.searchTerm }); - lastOffset = offset + this.props.searchTerm.length; - } - bodyList.push({ originalBody.substring(lastOffset) }); - } - body = bodyList; - } - else { - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - body = ; - } - else { - body = originalBody; - } - } - - return ( - - { body } - - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MemberInfo.js b/src/skins/vector/views/molecules/MemberInfo.js deleted file mode 100644 index 24fa1e91a4..0000000000 --- a/src/skins/vector/views/molecules/MemberInfo.js +++ /dev/null @@ -1,101 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var sdk = require('matrix-react-sdk') -var dis = require('matrix-react-sdk/lib/dispatcher'); -var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo') - -// FIXME: this should probably be an organism, to match with MemberList, not a molecule - -module.exports = React.createClass({ - displayName: 'MemberInfo', - mixins: [MemberInfoController], - - onCancel: function(e) { - dis.dispatch({ - action: "view_user", - member: null - }); - }, - - render: function() { - var interactButton, kickButton, banButton, muteButton, giveModButton, spinner; - if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) { - interactButton =
    Leave room
    ; - } - else { - interactButton =
    Start chat
    ; - } - - if (this.state.creatingRoom) { - var Loader = sdk.getComponent("atoms.Spinner"); - spinner = ; - } - - if (this.state.can.kick) { - kickButton =
    - Kick -
    ; - } - if (this.state.can.ban) { - banButton =
    - Ban -
    ; - } - if (this.state.can.mute) { - var muteLabel = this.state.muted ? "Unmute" : "Mute"; - muteButton =
    - {muteLabel} -
    ; - } - if (this.state.can.modifyLevel) { - var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod"; - giveModButton =
    - {giveOpLabel} -
    - } - - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( -
    - -
    - -
    -

    { this.props.member.name }

    -
    - { this.props.member.userId } -
    -
    - power: { this.props.member.powerLevelNorm }% -
    -
    - {interactButton} - {muteButton} - {kickButton} - {banButton} - {giveModButton} - {spinner} -
    -
    - ); - } -}); diff --git a/src/skins/vector/views/molecules/MemberTile.js b/src/skins/vector/views/molecules/MemberTile.js deleted file mode 100644 index 25ba3db94c..0000000000 --- a/src/skins/vector/views/molecules/MemberTile.js +++ /dev/null @@ -1,180 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var sdk = require('matrix-react-sdk') -var dis = require('matrix-react-sdk/lib/dispatcher'); -var MemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MemberTile') - -// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them. -// Revert to Arial when this happens, which on OSX works at least. -var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/; - -module.exports = React.createClass({ - displayName: 'MemberTile', - mixins: [MemberTileController], - - shouldComponentUpdate: function(nextProps, nextState) { - if (this.state.hover !== nextState.hover) return true; - if ( - this.member_last_modified_time === undefined || - this.member_last_modified_time < nextProps.member.getLastModifiedTime() - ) { - return true - } - if ( - nextProps.member.user && - (this.user_last_modified_time === undefined || - this.user_last_modified_time < nextProps.member.user.getLastModifiedTime()) - ) { - return true - } - return false; - }, - - mouseEnter: function(e) { - this.setState({ 'hover': true }); - }, - - mouseLeave: function(e) { - this.setState({ 'hover': false }); - }, - - onClick: function(e) { - dis.dispatch({ - action: 'view_user', - member: this.props.member, - }); - }, - - getDuration: function(time) { - if (!time) return; - var t = parseInt(time / 1000); - var s = t % 60; - var m = parseInt(t / 60) % 60; - var h = parseInt(t / (60 * 60)) % 24; - var d = parseInt(t / (60 * 60 * 24)); - if (t < 60) { - if (t < 0) { - return "0s"; - } - return s + "s"; - } - if (t < 60 * 60) { - return m + "m"; - } - if (t < 24 * 60 * 60) { - return h + "h"; - } - return d + "d "; - }, - - getPrettyPresence: function(user) { - if (!user) return "Unknown"; - var presence = user.presence; - if (presence === "online") return "Online"; - if (presence === "unavailable") return "Idle"; // XXX: is this actually right? - if (presence === "offline") return "Offline"; - return "Unknown"; - }, - - getPowerLabel: function() { - var label = this.props.member.userId; - if (this.state.isTargetMod) { - label += " - Mod (" + this.props.member.powerLevelNorm + "%)"; - } - return label; - }, - - render: function() { - this.member_last_modified_time = this.props.member.getLastModifiedTime(); - if (this.props.member.user) { - this.user_last_modified_time = this.props.member.user.getLastModifiedTime(); - } - - var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId; - - var power; - // if (this.props.member && this.props.member.powerLevelNorm > 0) { - // var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png"; - // power = ; - // } - var presenceClass = "mx_MemberTile_offline"; - var mainClassName = "mx_MemberTile "; - if (this.props.member.user) { - if (this.props.member.user.presence === "online") { - presenceClass = "mx_MemberTile_online"; - } - else if (this.props.member.user.presence === "unavailable") { - presenceClass = "mx_MemberTile_unavailable"; - } - } - mainClassName += presenceClass; - if (this.state.hover) { - mainClassName += " mx_MemberTile_hover"; - } - - var name = this.props.member.name; - // if (isMyUser) name += " (me)"; // this does nothing other than introduce line wrapping and pain - //var leave = isMyUser ? : null; - - var nameClass = "mx_MemberTile_name"; - if (zalgo.test(name)) { - nameClass += " mx_MemberTile_zalgo"; - } - - var nameEl; - if (this.state.hover) { - var presence; - // FIXME: make presence data update whenever User.presence changes... - var active = this.props.member.user ? ((Date.now() - (this.props.member.user.lastPresenceTs - this.props.member.user.lastActiveAgo)) || -1) : -1; - if (active >= 0) { - presence =
    { this.getPrettyPresence(this.props.member.user) } { this.getDuration(active) } ago
    ; - } - else { - presence =
    { this.getPrettyPresence(this.props.member.user) }
    ; - } - - nameEl = -
    - -
    { name }
    - { presence } -
    - } - else { - nameEl = -
    - { name } -
    - } - - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( -
    -
    - - { power } -
    - { nameEl } -
    - ); - } -}); diff --git a/src/skins/vector/views/molecules/MessageComposer.js b/src/skins/vector/views/molecules/MessageComposer.js deleted file mode 100644 index 2f0e7ac57f..0000000000 --- a/src/skins/vector/views/molecules/MessageComposer.js +++ /dev/null @@ -1,79 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var MessageComposerController = require('matrix-react-sdk/lib/controllers/molecules/MessageComposer') - -var sdk = require('matrix-react-sdk') -var dis = require('matrix-react-sdk/lib/dispatcher') - -module.exports = React.createClass({ - displayName: 'MessageComposer', - mixins: [MessageComposerController], - - onUploadClick: function(ev) { - this.refs.uploadInput.click(); - }, - - onUploadFileSelected: function(ev) { - var files = ev.target.files; - // MessageComposer shouldn't have to rely on it's parent passing in a callback to upload a file - if (files && files.length > 0) { - this.props.uploadFile(files[0]); - } - this.refs.uploadInput.value = null; - }, - - onCallClick: function(ev) { - dis.dispatch({ - action: 'place_call', - type: ev.shiftKey ? "screensharing" : "video", - room_id: this.props.room.roomId - }); - }, - - render: function() { - var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); - var uploadInputStyle = {display: 'none'}; - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( -
    -
    -
    -
    - -
    -
    -
    - cancel_button =
    Cancel
    - save_button =
    Save Changes
    - } else { - // - name = -
    -
    { this.props.room.name }
    -
    - -
    -
    - if (topic) topic_el =
    { topic.getContent().topic }
    ; - } - - var roomAvatar = null; - if (this.props.room) { - roomAvatar = ( - - ); - } - - var zoom_button, video_button, voice_button; - if (activeCall) { - if (activeCall.type == "video") { - zoom_button = ( -
    - Fullscreen -
    - ); - } - video_button = -
    - Video call -
    ; - voice_button = -
    - VoIP call -
    ; - } - - header = -
    -
    -
    - { roomAvatar } -
    -
    - { name } - { topic_el } -
    -
    - {call_buttons} - {cancel_button} - {save_button} -
    - { video_button } - { voice_button } - { zoom_button } -
    - Search -
    -
    -
    - } - - return ( -
    - { header } -
    - ); - }, -}); diff --git a/src/skins/vector/views/molecules/RoomSettings.js b/src/skins/vector/views/molecules/RoomSettings.js deleted file mode 100644 index 4fdd40d9e1..0000000000 --- a/src/skins/vector/views/molecules/RoomSettings.js +++ /dev/null @@ -1,232 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var sdk = require('matrix-react-sdk'); - -var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings') - -module.exports = React.createClass({ - displayName: 'RoomSettings', - mixins: [RoomSettingsController], - - getTopic: function() { - return this.refs.topic.value; - }, - - getJoinRules: function() { - return this.refs.is_private.checked ? "invite" : "public"; - }, - - getHistoryVisibility: function() { - return this.refs.share_history.checked ? "shared" : "invited"; - }, - - getPowerLevels: function() { - if (!this.state.power_levels_changed) return undefined; - - var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); - power_levels = power_levels.getContent(); - - var new_power_levels = { - ban: parseInt(this.refs.ban.value), - kick: parseInt(this.refs.kick.value), - redact: parseInt(this.refs.redact.value), - invite: parseInt(this.refs.invite.value), - events_default: parseInt(this.refs.events_default.value), - state_default: parseInt(this.refs.state_default.value), - users_default: parseInt(this.refs.users_default.value), - users: power_levels.users, - events: power_levels.events, - }; - - return new_power_levels; - }, - - onPowerLevelsChanged: function() { - this.setState({ - power_levels_changed: true - }); - }, - - render: function() { - var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar'); - - var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - if (topic) topic = topic.getContent().topic; - - var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', ''); - if (join_rule) join_rule = join_rule.getContent().join_rule; - - var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', ''); - if (history_visibility) history_visibility = history_visibility.getContent().history_visibility; - - var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); - - var events_levels = power_levels.events || {}; - - if (power_levels) { - power_levels = power_levels.getContent(); - - var ban_level = parseInt(power_levels.ban); - var kick_level = parseInt(power_levels.kick); - var redact_level = parseInt(power_levels.redact); - var invite_level = parseInt(power_levels.invite || 0); - var send_level = parseInt(power_levels.events_default || 0); - var state_level = parseInt(power_levels.state_default || 0); - var default_user_level = parseInt(power_levels.users_default || 0); - - if (power_levels.ban == undefined) ban_level = 50; - if (power_levels.kick == undefined) kick_level = 50; - if (power_levels.redact == undefined) redact_level = 50; - - var user_levels = power_levels.users || {}; - - var user_id = MatrixClientPeg.get().credentials.userId; - - var current_user_level = user_levels[user_id]; - if (current_user_level == undefined) current_user_level = default_user_level; - - var power_level_level = events_levels["m.room.power_levels"]; - if (power_level_level == undefined) { - power_level_level = state_level; - } - - var can_change_levels = current_user_level >= power_level_level; - } else { - var ban_level = 50; - var kick_level = 50; - var redact_level = 50; - var invite_level = 0; - var send_level = 0; - var state_level = 0; - var default_user_level = 0; - - var user_levels = []; - var events_levels = []; - - var current_user_level = 0; - - var power_level_level = 0; - - var can_change_levels = false; - } - - var room_avatar_level = parseInt(power_levels.state_default || 0); - if (events_levels['m.room.avatar'] !== undefined) { - room_avatar_level = events_levels['m.room.avatar']; - } - var can_set_room_avatar = current_user_level >= room_avatar_level; - - var change_avatar; - if (can_set_room_avatar) { - change_avatar =
    -

    Room Icon

    - -
    ; - } - - var banned = this.props.room.getMembersWithMembership("ban"); - - return ( -
    -