diff --git a/.gitignore b/.gitignore index 13466ce8..2bc43f86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ node_modules vector/bundle.* lib +.DS_Store +key.pem +cert.pem +vector/components.css +packages/ diff --git a/.modernizr.json b/.modernizr.json new file mode 100644 index 00000000..29e620a5 --- /dev/null +++ b/.modernizr.json @@ -0,0 +1,14 @@ +{ + "minify": true, + "classPrefix": "modernizr_", + "options": [ + "setClasses" + ], + "feature-detects": [ + "test/css/displaytable", + "test/css/flexbox", + "test/es5/specification", + "test/css/objectfit", + "test/storage/localstorage" + ] +} \ No newline at end of file diff --git a/AUTHORS.rst b/AUTHORS.rst index 8617b589..c08ad5e6 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,3 +7,6 @@ include: * https://github.com/neko259 Improved scrollbar CSS + +* Florent VIOLLEAU (https://github.com/floviolleau) + Improve README.md for a better understanding of installation instructions diff --git a/README.md b/README.md index 7bf2bea3..16d4b056 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,37 @@ Getting started =============== 1. Install or update `node.js` so that your `npm` is at least at version `2.0.0` -2. Clone the repo: `git clone https://github.com/vector-im/vector-web.git` -3. Switch to the SDK directory: `cd vector-web` -4. Install the prerequisites: `npm install` -5. Start the development builder and a testing server: `npm start` -6. Wait a few seconds for the initial build to finish. -7. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector. +1. Clone the repo: `git clone https://github.com/vector-im/vector-web.git` +1. Switch to the vector directory: `cd vector-web` +1. Install the prerequisites: `npm install` +1. Start the development builder and a testing server: `npm start` +1. Wait a few seconds for the initial build to finish (the command won't + terminate: it's running a web server for you). +1. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector. With `npm start`, any changes you make to the source files will cause a rebuild so -your changes will show up when you refresh. +your changes will show up when you refresh. This development server also disables +caching, so do NOT use it in production. + +Configuring +=========== + +Configure the app by modifying the `config.json` file: + +1. `default_hs_url` is the default home server url. +1. `default_is_url` is the default identity server url (this is the server used + for verifying third party identifiers like email addresses). If this is blank, + registering with an email address or adding an email address to your account + will not work. + +You will need to re-run `npm run build` after editing `config.json`. + +Deployment +========== For production use, run `npm run build` to build all the necessary files -into the `vector` directory and run your own server. +into the `vector` directory. You can then mount the vector directory on +your webserver to actually serve up the app, which is entirely static content. Development =========== @@ -30,31 +49,89 @@ setup above, and your changes will cause an instant rebuild. However, all serious development on Vector happens on the `develop` branch. This typically depends on the `develop` snapshot versions of `matrix-react-sdk` and `matrix-js-sdk` -too, which isn't expressed in Vector's `package.json`. To do this, check out -the `develop` branches of these libraries and then use `npm link` to tell Vector -about them: +too, which can't be installed automatically due to https://github.com/npm/npm/issues/3055. +To get the right dependencies, check out the `develop` branches of these libraries and +then use `ln -s` to tell Vector about them: + +[Be aware that there may be problems with this process under npm version 3.] + +First clone and build `matrix-js-sdk`: + +1. `git clone git@github.com:matrix-org/matrix-js-sdk.git` +1. `pushd matrix-js-sdk` +1. `git checkout develop` +1. `npm install` +1. `npm install source-map-loader` # because webpack is made of fail (https://github.com/webpack/webpack/issues/1472) +1. `popd` + +Then similarly with `matrix-react-sdk`: 1. `git clone git@github.com:matrix-org/matrix-react-sdk.git` -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: - `npm link path/to/your/react/sdk` +1. `pushd matrix-react-sdk` +1. `git checkout develop` +1. `npm install` +1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/` +1. `popd` -Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk` -directory. +Finally, build and start vector itself: + +1. `git clone git@github.com:vector-im/vector-web.git` +1. `cd vector-web` +1. `git checkout develop` +1. `npm install` +1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/` +1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/` +1. `npm start` +1. Wait a few seconds for the initial build to finish; you should see something like: + + ``` + Hash: b0af76309dd56d7275c8 + Version: webpack 1.12.14 + Time: 14533ms + Asset Size Chunks Chunk Names + bundle.js 4.2 MB 0 [emitted] main + bundle.css 91.5 kB 0 [emitted] main + bundle.js.map 5.29 MB 0 [emitted] main + bundle.css.map 116 kB 0 [emitted] main + + 1013 hidden modules + ``` + Remember, the command will not terminate since it runs the web server + and rebuilds source files when they change. +1. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector. + +When you make changes to `matrix-js-sdk` or `matrix-react-sdk`, you will need +to run `npm run build` in the relevant directory. You can do this automatically +by instead running `npm start` in each directory, to start a development +builder which will watch for changes to the files and rebuild automatically. If you add or remove any components from the Vector skin, you will need to rebuild the skin's index by running, `npm run reskindex`. -You may need to run `npm i source-map-loader` in matrix-js-sdk if you get errors -about "Cannot resolve module 'source-map-loader'" due to shortcomings in webpack. +Enabling encryption +=================== -Deployment -========== +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. -Just run `npm build` and then mount the `vector` directory on your webserver to -actually serve up the app, which is entirely static content. +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/deploy/redeploy.py b/deploy/redeploy.py new file mode 100755 index 00000000..fd81929a --- /dev/null +++ b/deploy/redeploy.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +from __future__ import print_function +import json, requests, tarfile, argparse, os, errno +from urlparse import urljoin +from flask import Flask, jsonify, request, abort +app = Flask(__name__) + +arg_jenkins_url, arg_extract_path, arg_should_clean, arg_symlink = ( + None, None, None, None +) + +def download_file(url): + local_filename = url.split('/')[-1] + r = requests.get(url, stream=True) + with open(local_filename, 'wb') as f: + for chunk in r.iter_content(chunk_size=1024): + if chunk: # filter out keep-alive new chunks + f.write(chunk) + return local_filename + +def untar_to(tarball, dest): + with tarfile.open(tarball) as tar: + tar.extractall(dest) + +def create_symlink(source, linkname): + try: + os.symlink(source, linkname) + except OSError, e: + if e.errno == errno.EEXIST: + # atomic modification + os.symlink(source, linkname + ".tmp") + os.rename(linkname + ".tmp", linkname) + else: + raise e + +@app.route("/", methods=["POST"]) +def on_receive_jenkins_poke(): + # { + # "name": "VectorWebDevelop", + # "build": { + # "number": 8 + # } + # } + incoming_json = request.get_json() + if not incoming_json: + abort(400, "No JSON provided!") + return + print("Incoming JSON: %s" % (incoming_json,)) + + job_name = incoming_json.get("name") + if not isinstance(job_name, basestring): + abort(400, "Bad job name: %s" % (job_name,)) + return + + build_num = incoming_json.get("build", {}).get("number", 0) + if not build_num or build_num <= 0 or not isinstance(build_num, int): + abort(400, "Missing or bad build number") + return + + artifact_url = urljoin( + arg_jenkins_url, "job/%s/%s/api/json" % (job_name, build_num) + ) + artifact_response = requests.get(artifact_url).json() + + # { + # "actions": [], + # "artifacts": [ + # { + # "displayPath": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz", + # "fileName": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz", + # "relativePath": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz" + # } + # ], + # "building": false, + # "description": null, + # "displayName": "#11", + # "duration": 137976, + # "estimatedDuration": 132008, + # "executor": null, + # "fullDisplayName": "VectorWebDevelop #11", + # "id": "11", + # "keepLog": false, + # "number": 11, + # "queueId": 12254, + # "result": "SUCCESS", + # "timestamp": 1454432640079, + # "url": "http://matrix.org/jenkins/job/VectorWebDevelop/11/", + # "builtOn": "", + # "changeSet": {}, + # "culprits": [] + # } + if artifact_response.get("result") != "SUCCESS": + abort(404, "Not deploying. Build was not marked as SUCCESS.") + return + + if len(artifact_response.get("artifacts", [])) != 1: + abort(404, "Not deploying. Build has an unexpected number of artifacts.") + return + + tar_gz_path = artifact_response["artifacts"][0]["relativePath"] + if not tar_gz_path.endswith(".tar.gz"): + abort(404, "Not deploying. Artifact is not a .tar.gz file") + return + + tar_gz_url = urljoin( + arg_jenkins_url, "job/%s/%s/artifact/%s" % (job_name, build_num, tar_gz_path) + ) + + print("Retrieving .tar.gz file: %s" % tar_gz_url) + filename = download_file(tar_gz_url) + print("Downloaded file: %s" % filename) + name_str = filename.replace(".tar.gz", "") + untar_location = os.path.join(arg_extract_path, name_str) + untar_to(filename, untar_location) + + if arg_should_clean: + os.remove(filename) + + # stamp the version somewhere JS can get to it + with open(os.path.join(untar_location, "vector/version"), "w") as stamp_file: + stamp_file.write(name_str) + + create_symlink(source=os.path.join(untar_location, "vector"), linkname=arg_symlink) + + return jsonify({}) + +if __name__ == "__main__": + parser = argparse.ArgumentParser("Runs a Vector redeployment server.") + parser.add_argument( + "-j", "--jenkins", dest="jenkins", default="http://matrix.org/jenkins/", help=( + "The base URL of the Jenkins web server. This will be hit to get the\ + built artifacts (the .gz file) for redeploying." + ) + ) + parser.add_argument( + "-p", "--port", dest="port", default=4000, type=int, help=( + "The port to listen on for requests from Jenkins." + ) + ) + parser.add_argument( + "-e", "--extract", dest="extract", default="./extracted", help=( + "The location to extract .tar.gz files to." + ) + ) + parser.add_argument( + "-c", "--clean", dest="clean", action="store_true", default=False, help=( + "Remove .tar.gz files after they have been downloaded and extracted." + ) + ) + parser.add_argument( + "-s", "--symlink", dest="symlink", default="./latest", help=( + "Write a symlink to this location pointing to the extracted tarball. \ + New builds will keep overwriting this symlink. The symlink will point \ + to the /vector directory INSIDE the tarball." + ) + ) + args = parser.parse_args() + if args.jenkins.endswith("/"): # important for urljoin + arg_jenkins_url = args.jenkins + else: + arg_jenkins_url = args.jenkins + "/" + arg_extract_path = args.extract + arg_should_clean = args.clean + arg_symlink = args.symlink + print( + "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s" % + (args.port, arg_extract_path, + " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url) + ) + app.run(host="0.0.0.0", port=args.port, debug=True) diff --git a/jenkins.sh b/jenkins.sh new file mode 100755 index 00000000..a421c4d2 --- /dev/null +++ b/jenkins.sh @@ -0,0 +1,14 @@ +#!/bin/bash -l +export NVM_DIR="/home/jenkins/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" +nvm use 4 +npm install +(cd node_modules/matrix-react-sdk && npm run build) # npm doesn't do this when dependencies point at github.com >:( +npm run build # Dumps artificats to /vector + +# gzip up ./vector +rm vector-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist +REACT_SHA=$(grep 'gitHead' node_modules/matrix-react-sdk/package.json | cut -d \" -f 4 | head -c 12) # node_modules deps from 'npm install' don't have a .git dir so can't rev-parse. +JSSDK_SHA=$(grep 'gitHead' node_modules/matrix-js-sdk/package.json | cut -d \" -f 4 | head -c 12) # But they do set the commit in package.json under 'gitHead' which we're grabbing here. +VECTOR_SHA=$(git rev-parse --short=12 HEAD) # use the ACTUAL SHA rather than assume develop +tar -zcvhf vector-$VECTOR_SHA-react-$REACT_SHA-js-$JSSDK_SHA.tar.gz vector #g[z]ip, [c]reate archive, [v]erbose, [f]ilename, [h]ard-dereference (do not archive symlinks) diff --git a/package.json b/package.json index cab45e83..81975f97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vector-web", - "version": "0.1.2", + "version": "0.2.0", "description": "Vector webapp", "author": "matrix.org", "repository": { @@ -9,40 +9,56 @@ }, "license": "Apache-2.0", "style": "bundle.css", + "matrix-react-parent": "matrix-react-sdk", "scripts": { - "reskindex": "reskindex vector -h src/skins/vector/header", - "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch", + "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/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", + "package": "npm run build && mkdir -p packages && tar chvzf packages/vector-`git describe --dirty || echo unknown`.tar.gz vector", "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:js:prod": "NODE_ENV=production webpack -w src/vector/index.js vector/bundle.js", + "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", + "start:prod": "parallelshell \"npm run start:js:prod\" \"npm run start:skins:css\" \"http-server -c 1 vector\"", + "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": { + "babel-polyfill": "^6.5.0", "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": "^0.4.0", + "matrix-react-sdk": "^0.1.0", + "modernizr": "^3.1.0", "q": "^1.4.1", - "react": "^0.13.3", - "react-loader": "^1.4.0" + "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.11.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.13" } } diff --git a/src/Avatar.js b/src/Avatar.js deleted file mode 100644 index afc5e9dd..00000000 --- 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 7865e45a..00000000 --- a/src/ContextualMenu.js +++ /dev/null @@ -1,81 +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'); - -// 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} - -
-
-
- ); - - React.render(menu, this.getOrCreateContainer()); - - return {close: closeMenu}; - }, -}; diff --git a/src/modules/VectorConferenceHandler.js b/src/VectorConferenceHandler.js similarity index 92% rename from src/modules/VectorConferenceHandler.js rename to src/VectorConferenceHandler.js index 637e34f9..aa88e77f 100644 --- a/src/modules/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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 00000000..b25b5ef9 --- /dev/null +++ b/src/component-index.js @@ -0,0 +1,50 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * 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.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar'); +module.exports.components['views.globals.MatrixToolbar'] = require('./components/views/globals/MatrixToolbar'); +module.exports.components['views.globals.NewVersionBar'] = require('./components/views/globals/NewVersionBar'); +module.exports.components['views.login.VectorCustomServerDialog'] = require('./components/views/login/VectorCustomServerDialog'); +module.exports.components['views.login.VectorLoginFooter'] = require('./components/views/login/VectorLoginFooter'); +module.exports.components['views.login.VectorLoginHeader'] = 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'); +module.exports.components['views.settings.Notifications'] = require('./components/views/settings/Notifications'); diff --git a/src/skins/vector/views/molecules/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js similarity index 80% rename from src/skins/vector/views/molecules/BottomLeftMenu.js rename to src/components/structures/BottomLeftMenu.js index 2020d29d..a4d89fcf 100644 --- a/src/skins/vector/views/molecules/BottomLeftMenu.js +++ b/src/components/structures/BottomLeftMenu.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -40,19 +40,19 @@ 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/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js new file mode 100644 index 00000000..6f2f93e6 --- /dev/null +++ b/src/components/structures/CompatibilityPage.js @@ -0,0 +1,62 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'CompatibilityPage', + propTypes: { + onAccept: React.PropTypes.func + }, + + getDefaultProps: function() { + return { + onAccept: function() {} // NOP + }; + }, + + onAccept: function() { + this.props.onAccept(); + }, + + render: function() { + + return ( +
+
+

Sorry, your browser is not able to run Vector.

+

+ Buttons and images may appear out of place, communication may + not be possible and all manner of chaos may be unleashed. +

+

+ Please install Chrome for + the best experience. +

+

+ Though if you like taking risks with your life, you can still try it + out by clicking that you understand the risks involved. +

+ +
+
+ ); + } +}); diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js new file mode 100644 index 00000000..c80c4818 --- /dev/null +++ b/src/components/structures/LeftPanel.js @@ -0,0 +1,125 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); +var DragDropContext = require('react-dnd').DragDropContext; +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({ + displayName: 'LeftPanel', + + getInitialState: function() { + return { + showCallElement: null, + }; + }, + + componentDidMount: function() { + this.dispatcherRef = dis.register(this.onAction); + }, + + componentWillReceiveProps: function(newProps) { + this._recheckCallElement(newProps.selectedRoom); + }, + + componentWillUnmount: function() { + dis.unregister(this.dispatcherRef); + }, + + onAction: function(payload) { + switch (payload.action) { + // listen for call state changes to prod the render method, which + // may hide the global CallView if the call it is tracking is dead + case 'call_state': + this._recheckCallElement(this.props.selectedRoom); + break; + } + }, + + _recheckCallElement: function(selectedRoomId) { + // if we aren't viewing a room with an ongoing call, but there is an + // active call, show the call element - we need to do this to make + // audio/video not crap out + var activeCall = CallHandler.getAnyActiveCall(); + var callForRoom = CallHandler.getCallForRoom(selectedRoomId); + var showCall = (activeCall && activeCall.call_state === 'connected' && !callForRoom); + this.setState({ + showCallElement: showCall + }); + }, + + onHideClick: function() { + dis.dispatch({ + action: 'hide_left_panel', + }); + }, + + onCallViewClick: function() { + var call = CallHandler.getAnyActiveCall(); + if (call) { + dis.dispatch({ + action: 'view_room', + room_id: call.roomId, + }); + } + }, + + render: function() { + var RoomList = sdk.getComponent('rooms.RoomList'); + var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu'); + + var collapseButton; + var classes = "mx_LeftPanel"; + if (this.props.collapsed) { + classes += " collapsed"; + } + else { + // Hide the collapse button until we work out how to display it in the new skin + // collapseButton = < + } + + var callPreview; + if (this.state.showCallElement && !this.props.collapsed) { + var CallView = sdk.getComponent('voip.CallView'); + callPreview = ( + + ); + } + + return ( + + ); + } +}); + +module.exports = DragDropContext(HTML5Backend)(LeftPanel); diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js new file mode 100644 index 00000000..dc4f606c --- /dev/null +++ b/src/components/structures/RightPanel.js @@ -0,0 +1,173 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); +var sdk = require('matrix-react-sdk') +var dis = require('matrix-react-sdk/lib/dispatcher'); +var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); +var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); + +module.exports = React.createClass({ + displayName: 'RightPanel', + + Phase : { + MemberList: 'MemberList', + FileList: 'FileList', + MemberInfo: 'MemberInfo', + }, + + componentWillMount: function() { + this.dispatcherRef = dis.register(this.onAction); + var cli = MatrixClientPeg.get(); + cli.on("RoomState.members", this.onRoomStateMember); + }, + + componentWillUnmount: function() { + dis.unregister(this.dispatcherRef); + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); + } + }, + + getInitialState: function() { + return { + phase : this.Phase.MemberList + } + }, + + onMemberListButtonClick: function() { + if (this.props.collapsed) { + this.setState({ phase: this.Phase.MemberList }); + dis.dispatch({ + action: 'show_right_panel', + }); + } + else { + dis.dispatch({ + action: 'hide_right_panel', + }); + } + }, + + onRoomStateMember: function(ev, state, member) { + // redraw the badge on the membership list + if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) { + this._delayedUpdate(); + } + 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._delayedUpdate(); + } + }, + + _delayedUpdate: new rate_limited_func(function() { + this.forceUpdate(); + }, 500), + + onAction: function(payload) { + if (payload.action === "view_user") { + if (payload.member) { + this.setState({ + phase: this.Phase.MemberInfo, + member: payload.member, + }); + } + else { + this.setState({ + phase: this.Phase.MemberList + }); + } + } + if (payload.action === "view_room") { + if (this.state.phase === this.Phase.MemberInfo) { + this.setState({ + phase: this.Phase.MemberList + }); + } + } + }, + + render: function() { + var MemberList = sdk.getComponent('rooms.MemberList'); + var TintableSvg = sdk.getComponent("elements.TintableSvg"); + var buttonGroup; + var panel; + + var filesHighlight; + var membersHighlight; + if (!this.props.collapsed) { + if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) { + membersHighlight =
; + } + else if (this.state.phase == this.Phase.FileList) { + filesHighlight =
; + } + } + + var membersBadge; + if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) { + var cli = MatrixClientPeg.get(); + var room = cli.getRoom(this.props.roomId); + if (room) { + membersBadge =
{ room.getJoinedMembers().length }
; + } + } + + if (this.props.roomId) { + buttonGroup = +
+
+ + { membersBadge } + { membersHighlight } +
+
+ + { filesHighlight } +
+
; + + if (!this.props.collapsed) { + if(this.state.phase == this.Phase.MemberList) { + panel = + } + else if(this.state.phase == this.Phase.MemberInfo) { + var MemberInfo = sdk.getComponent('rooms.MemberInfo'); + panel = + } + } + + } + + var classes = "mx_RightPanel"; + if (this.props.collapsed) { + classes += " collapsed"; + } + + return ( + + ); + } +}); + diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js new file mode 100644 index 00000000..6cc90f79 --- /dev/null +++ b/src/components/structures/RoomDirectory.js @@ -0,0 +1,176 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); + +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var ContentRepo = require("matrix-js-sdk").ContentRepo; +var Modal = require('matrix-react-sdk/lib/Modal'); +var sdk = require('matrix-react-sdk') +var dis = require('matrix-react-sdk/lib/dispatcher'); +var GeminiScrollbar = require('react-gemini-scrollbar'); + +var linkify = require('linkifyjs'); +var linkifyString = require('linkifyjs/string'); +var linkifyMatrix = require('matrix-react-sdk/lib/linkify-matrix'); +var sanitizeHtml = require('sanitize-html'); + +linkifyMatrix(linkify); + +module.exports = React.createClass({ + displayName: 'RoomDirectory', + + getInitialState: function() { + return { + publicRooms: [], + roomAlias: '', + loading: true, + } + }, + + componentDidMount: function() { + var self = this; + MatrixClientPeg.get().publicRooms(function (err, data) { + if (err) { + self.setState({ loading: false }); + console.error("Failed to get publicRooms: %s", JSON.stringify(err)); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to get public room list", + description: err.message + }); + } + else { + self.setState({ + publicRooms: data.chunk, + loading: false, + }); + self.forceUpdate(); + } + }); + }, + + showRoom: function(roomId) { + dis.dispatch({ + action: 'view_room', + room_id: roomId + }); + }, + + getRows: function(filter) { + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + + if (!this.state.publicRooms) return []; + + var rooms = this.state.publicRooms.filter(function(a) { + // FIXME: if incrementally typing, keep narrowing down the search set + // incrementally rather than starting over each time. + return (a.aliases[0].toLowerCase().search(filter.toLowerCase()) >= 0 && a.num_joined_members > 0); + }).sort(function(a,b) { + return a.num_joined_members - b.num_joined_members; + }); + var rows = []; + var self = this; + var guestRead, guestJoin, perms; + for (var i = 0; i < rooms.length; i++) { + var name = rooms[i].name || rooms[i].aliases[0]; + guestRead = null; + guestJoin = null; + + if (rooms[i].world_readable) { + guestRead = ( +
World readable
+ ); + } + if (rooms[i].guest_can_join) { + guestJoin = ( +
Guests can join
+ ); + } + + perms = null; + if (guestRead || guestJoin) { + perms =
{guestRead} {guestJoin}
; + } + + var topic = rooms[i].topic || ''; + topic = linkifyString(sanitizeHtml(topic)); + + rows.unshift( + + + + + +
{ name }
  + { perms } +
+
{ rooms[i].aliases[0] }
+ + + { rooms[i].num_joined_members } + + + ); + } + return rows; + }, + + onKeyUp: function(ev) { + this.forceUpdate(); + this.setState({ roomAlias : this.refs.roomAlias.value }) + if (ev.key == "Enter") { + this.showRoom(this.refs.roomAlias.value); + } + }, + + render: function() { + if (this.state.loading) { + var Loader = sdk.getComponent("elements.Spinner"); + return ( +
+ +
+ ); + } + + var RoomHeader = sdk.getComponent('rooms.RoomHeader'); + return ( +
+ +
+ + + + + { this.getRows(this.state.roomAlias) } + +
+
+
+
+ ); + } +}); + diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js new file mode 100644 index 00000000..2eeec2a6 --- /dev/null +++ b/src/components/structures/RoomSubList.js @@ -0,0 +1,364 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +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 Unread = require('matrix-react-sdk/lib/Unread'); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); + +// turn this on for drop & drag console debugging galore +var debug = false; + +var roomListTarget = { + canDrop: function() { + return true; + }, + + drop: function(props, monitor, component) { + if (debug) console.log("dropped on sublist") + }, + + hover: function(props, monitor, component) { + var item = monitor.getItem(); + + if (component.state.sortedList.length == 0 && props.editable) { + if (debug) console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver()); + + if (item.targetList !== component) { + item.targetList.removeRoomTile(item.room); + item.targetList = component; + } + + component.moveRoomTile(item.room, 0); + } + }, +}; + +var RoomSubList = React.createClass({ + displayName: 'RoomSubList', + + debug: debug, + + propTypes: { + list: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, + label: React.PropTypes.string.isRequired, + tagName: React.PropTypes.string, + editable: React.PropTypes.bool, + order: React.PropTypes.string.isRequired, + selectedRoom: React.PropTypes.string.isRequired, + startAsHidden: React.PropTypes.bool, + showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded + + // TODO: Fix the name of this. This is too easily confused with the + // "hidden" state which is the expanded (or not) view of the list of rooms. + // What this prop *really* does is control whether the room name is displayed + // so it should be named as such. + collapsed: React.PropTypes.bool.isRequired, + onHeaderClick: React.PropTypes.func, + alwaysShowHeader: React.PropTypes.bool, + incomingCall: React.PropTypes.object, + onShowMoreRooms: React.PropTypes.func + }, + + getInitialState: function() { + return { + hidden: this.props.startAsHidden || false, + truncateAt: 20, + sortedList: [], + }; + }, + + getDefaultProps: function() { + return { + onHeaderClick: function() {}, // NOP + onShowMoreRooms: function() {} // NOP + }; + }, + + componentWillMount: function() { + this.sortList(this.props.list, this.props.order); + }, + + componentWillReceiveProps: function(newProps) { + // order the room list appropriately before we re-render + //if (debug) console.log("received new props, list = " + newProps.list); + this.sortList(newProps.list, newProps.order); + }, + + onClick: function(ev) { + var isHidden = !this.state.hidden; + this.setState({ hidden : isHidden }); + + if (isHidden) { + // as good a way as any to reset the truncate state + this.setState({ truncateAt : 20 }); + this.props.onShowMoreRooms(); + } + + this.props.onHeaderClick(isHidden); + }, + + tsOfNewestEvent: function(room) { + for (var i = room.timeline.length - 1; i >= 0; --i) { + var ev = room.timeline[i]; + if (Unread.eventTriggersUnreadCount(ev) || + (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId)) + { + return ev.getTs(); + } + } + + // 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; + } + }, + + // TODO: factor the comparators back out into a generic comparator + // so that view_prev_room and view_next_room can do the right thing + + recentsComparator: function(roomA, roomB) { + return this.tsOfNewestEvent(roomB) - this.tsOfNewestEvent(roomA); + }, + + manualComparator: function(roomA, roomB) { + if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0; + var a = roomA.tags[this.props.tagName].order; + var b = roomB.tags[this.props.tagName].order; + return a == b ? this.recentsComparator(roomA, roomB) : ( a > b ? 1 : -1); + }, + + sortList: function(list, order) { + if (list === undefined) list = this.state.sortedList; + if (order === undefined) order = this.props.order; + var comparator; + list = list || []; + if (order === "manual") comparator = this.manualComparator; + if (order === "recent") comparator = this.recentsComparator; + + //if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list); + this.setState({ sortedList: list.sort(comparator) }); + }, + + moveRoomTile: function(room, atIndex) { + if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex); + //console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms)); + var found = this.findRoomTile(room); + var rooms = this.state.sortedList; + if (found.room) { + if (debug) console.log("removing at index " + found.index + " and adding at index " + atIndex); + rooms.splice(found.index, 1); + rooms.splice(atIndex, 0, found.room); + } + else { + if (debug) console.log("Adding at index " + atIndex); + rooms.splice(atIndex, 0, room); + } + this.setState({ sortedList: rooms }); + // console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms)); + }, + + // XXX: this isn't invoked via a property method but indirectly via + // the roomList property method. Unsure how evil this is. + removeRoomTile: function(room) { + if (debug) console.log("remove room " + room.roomId); + var found = this.findRoomTile(room); + var rooms = this.state.sortedList; + if (found.room) { + rooms.splice(found.index, 1); + } + else { + console.warn("Can't remove room " + room.roomId + " - can't find it"); + } + this.setState({ sortedList: rooms }); + }, + + findRoomTile: function(room) { + var index = this.state.sortedList.indexOf(room); + if (index >= 0) { + // console.log("found: room: " + room.roomId + " with index " + index); + } + else { + if (debug) console.log("didn't find room"); + room = null; + } + return ({ + room: room, + index: index, + }); + }, + + calcManualOrderTagData: function(room) { + var index = this.state.sortedList.indexOf(room); + + // we sort rooms by the lexicographic ordering of the 'order' metadata on their tags. + // for convenience, we calculate this for now a floating point number between 0.0 and 1.0. + + var orderA = 0.0; // by default we're next to the beginning of the list + if (index > 0) { + var prevTag = this.state.sortedList[index - 1].tags[this.props.tagName]; + if (!prevTag) { + console.error("Previous room in sublist is not tagged to be in this list. This should never happen.") + } + else if (prevTag.order === undefined) { + console.error("Previous room in sublist has no ordering metadata. This should never happen."); + } + else { + orderA = prevTag.order; + } + } + + var orderB = 1.0; // by default we're next to the end of the list too + if (index < this.state.sortedList.length - 1) { + var nextTag = this.state.sortedList[index + 1].tags[this.props.tagName]; + if (!nextTag) { + console.error("Next room in sublist is not tagged to be in this list. This should never happen.") + } + else if (nextTag.order === undefined) { + console.error("Next room in sublist has no ordering metadata. This should never happen."); + } + else { + orderB = nextTag.order; + } + } + + var order = (orderA + orderB) / 2.0; + if (order === orderA || order === orderB) { + console.error("Cannot describe new list position. This should be incredibly unlikely."); + // TODO: renumber the list + } + + return order; + }, + + makeRoomTiles: function() { + var self = this; + 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? + return ( + 0 || self.props.label === 'Invites' } + isInvite={ self.props.label === 'Invites' } + incomingCall={ self.props.incomingCall && (self.props.incomingCall.roomId === room.roomId) ? self.props.incomingCall : null } /> + ); + }); + }, + + _getHeaderJsx: function() { + var TintableSvg = sdk.getComponent("elements.TintableSvg"); + return ( +

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

+ ); + }, + + _createOverflowTile: function(overflowCount, totalCount) { + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + // XXX: this is duplicated from RoomTile - factor it out + return ( +
+
+ +
+
and { overflowCount } others...
+
+ ); + }, + + _showFullMemberList: function() { + this.setState({ + truncateAt: -1 + }); + this.props.onShowMoreRooms(); + }, + + render: function() { + var connectDropTarget = this.props.connectDropTarget; + var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); + var TruncatedList = sdk.getComponent('elements.TruncatedList'); + + var label = this.props.collapsed ? null : this.props.label; + + //console.log("render: " + JSON.stringify(this.state.sortedList)); + + var target; + if (this.state.sortedList.length == 0 && this.props.editable) { + target = ; + } + + if (this.state.sortedList.length > 0 || this.props.editable) { + var subList; + var classes = "mx_RoomSubList"; + + if (!this.state.hidden) { + subList = + { target } + { this.makeRoomTiles() } + ; + } + else { + subList = + ; + } + + return connectDropTarget( +
+ { this._getHeaderJsx() } + { subList } +
+ ); + } + else { + var Loader = sdk.getComponent("elements.Spinner"); + return ( +
+ { this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined } + { (this.props.showSpinner && !this.state.hidden) ? : undefined } +
+ ); + } + } +}); + +// Export the wrapped version, inlining the 'collect' functions +// to more closely resemble the ES7 +module.exports = +DropTarget('RoomTile', roomListTarget, function(connect) { + return { + connectDropTarget: connect.dropTarget(), + } +})(RoomSubList); diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js new file mode 100644 index 00000000..179f7553 --- /dev/null +++ b/src/components/structures/ViewSource.js @@ -0,0 +1,54 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'ViewSource', + + propTypes: { + onFinished: React.PropTypes.func.isRequired + }, + + componentDidMount: function() { + document.addEventListener("keydown", this.onKeyDown); + }, + + componentWillUnmount: function() { + document.removeEventListener("keydown", this.onKeyDown); + }, + + onKeyDown: function(ev) { + if (ev.keyCode == 27) { // escape + ev.stopPropagation(); + ev.preventDefault(); + this.props.onFinished(); + } + }, + + render: function() { + return ( +
+
+                    {JSON.stringify(this.props.mxEvent.event, null, 2)}
+                
+
+ ); + } +}); + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js new file mode 100644 index 00000000..ef00b387 --- /dev/null +++ b/src/components/views/elements/ImageView.js @@ -0,0 +1,154 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); + +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); + +var DateUtils = require('matrix-react-sdk/lib/DateUtils'); +var filesize = require('filesize'); + +module.exports = React.createClass({ + displayName: 'ImageView', + + propTypes: { + onFinished: React.PropTypes.func.isRequired + }, + + // XXX: keyboard shortcuts for managing dialogs should be done by the modal + // dialog base class somehow, surely... + componentDidMount: function() { + document.addEventListener("keydown", this.onKeyDown); + }, + + componentWillUnmount: function() { + document.removeEventListener("keydown", this.onKeyDown); + }, + + onKeyDown: function(ev) { + if (ev.keyCode == 27) { // escape + ev.stopPropagation(); + ev.preventDefault(); + this.props.onFinished(); + } + }, + + onRedactClick: function() { + var self = this; + MatrixClientPeg.get().redactEvent( + this.props.mxEvent.getRoomId(), this.props.mxEvent.getId() + ).done(function() { + if (self.props.onFinished) self.props.onFinished(); + }, function(e) { + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + // display error message stating you couldn't delete this. + var code = e.errcode || e.statusCode; + Modal.createDialog(ErrorDialog, { + title: "Error", + description: "You cannot delete this image. (" + code + ")" + }); + }); + }, + + render: function() { + +/* + // In theory max-width: 80%, max-height: 80% on the CSS should work + // but in practice, it doesn't, so do it manually: + + var width = this.props.width || 500; + var height = this.props.height || 500; + + var maxWidth = document.documentElement.clientWidth * 0.8; + var maxHeight = document.documentElement.clientHeight * 0.8; + + var widthFrac = width / maxWidth; + var heightFrac = height / maxHeight; + + var displayWidth; + var displayHeight; + if (widthFrac > heightFrac) { + displayWidth = Math.min(width, maxWidth); + displayHeight = (displayWidth / width) * height; + } else { + displayHeight = Math.min(height, maxHeight); + displayWidth = (displayHeight / height) * width; + } + + var style = { + width: displayWidth, + height: displayHeight + }; +*/ + var style, res; + + if (this.props.width && this.props.height) { + style = { + width: this.props.width, + height: this.props.height, + }; + 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 ( +
+
+
+
+ +
+
+ Close +
+
+
+ { this.props.mxEvent.getContent().body } +
+
+ Uploaded on { DateUtils.formatDate(new Date(this.props.mxEvent.getTs())) } by { this.props.mxEvent.getSender() } +
+ +
+ Download this file
+ { size } { res } +
+
+ +
+ Redact +
+
+
+
+
+
+
+
+
+ ); + } +}); diff --git a/src/skins/vector/views/atoms/Spinner.js b/src/components/views/elements/Spinner.js similarity index 92% rename from src/skins/vector/views/atoms/Spinner.js rename to src/components/views/elements/Spinner.js index 908f2678..2b620f12 100644 --- a/src/skins/vector/views/atoms/Spinner.js +++ b/src/components/views/elements/Spinner.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ module.exports = React.createClass({ var h = this.props.h || 32; var imgClass = this.props.imgClassName || ""; return ( -
+
); diff --git a/src/components/views/globals/GuestWarningBar.js b/src/components/views/globals/GuestWarningBar.js new file mode 100644 index 00000000..88940f35 --- /dev/null +++ b/src/components/views/globals/GuestWarningBar.js @@ -0,0 +1,44 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +var React = require('react'); +var dis = require('matrix-react-sdk/lib/dispatcher') + +module.exports = React.createClass({ + displayName: 'GuestWarningBar', + + onRegisterClicked: function() { + dis.dispatch({'action': 'logout'}); + dis.dispatch({'action': 'start_registration'}); + }, + + onLoginClicked: function() { + dis.dispatch({'action': 'logout'}); + dis.dispatch({'action': 'start_login'}); + }, + + render: function() { + return ( +
+ /!\ +
+ You are using Vector as a guest. Register or log in to access more rooms and features. +
+
+ ); + } +}); + diff --git a/src/skins/vector/views/molecules/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js similarity index 63% rename from src/skins/vector/views/molecules/MatrixToolbar.js rename to src/components/views/globals/MatrixToolbar.js index 4a299f14..b176ff2e 100644 --- a/src/skins/vector/views/molecules/MatrixToolbar.js +++ b/src/components/views/globals/MatrixToolbar.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,23 +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() { + Notifier.setEnabled(true); + }, + render: function() { - var EnableNotificationsButton = sdk.getComponent("atoms.EnableNotificationsButton"); return (
- You are not receiving desktop notifications. -
+ /!\ +
+ You are not receiving desktop notifications. Enable them now +
+
); } diff --git a/src/skins/vector/views/molecules/RoomDropTarget.js b/src/components/views/globals/NewVersionBar.js similarity index 63% rename from src/skins/vector/views/molecules/RoomDropTarget.js rename to src/components/views/globals/NewVersionBar.js index b1e15077..83dccf5d 100644 --- a/src/skins/vector/views/molecules/RoomDropTarget.js +++ b/src/components/views/globals/NewVersionBar.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,17 +17,20 @@ limitations under the License. 'use strict'; var React = require('react'); - -//var RoomDropTargetController = require('matrix-react-sdk/lib/controllers/molecules/RoomDropTargetController') +var sdk = require('matrix-react-sdk') module.exports = React.createClass({ - displayName: 'RoomDropTarget', - // mixins: [RoomDropTargetController], + displayName: 'NewVersionBar', + render: function() { return ( -
- {this.props.text} +
+ /!\ +
+ A new version of Vector is available. Refresh your browser. +
); } }); + diff --git a/src/components/views/login/VectorCustomServerDialog.js b/src/components/views/login/VectorCustomServerDialog.js new file mode 100644 index 00000000..b3e99a97 --- /dev/null +++ b/src/components/views/login/VectorCustomServerDialog.js @@ -0,0 +1,53 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +var React = require("react"); + +module.exports = React.createClass({ + displayName: 'VectorCustomServerDialog', + statics: { + replaces: 'CustomServerDialog', + }, + + render: function() { + return ( +
+
+ Custom Server Options +
+
+ + You can use the custom server options to log into other Matrix + servers by specifying a different Home server URL. +
+ This allows you to use Vector with an existing Matrix account on + a different Home server. +
+
+ You can also set a custom Identity server but this will affect + people's ability to find you if you use a server in a group other + than the main Matrix.org group. +
+
+
+ +
+
+ ); + } +}); diff --git a/src/skins/vector/views/molecules/EventAsTextTile.js b/src/components/views/login/VectorLoginFooter.js similarity index 57% rename from src/skins/vector/views/molecules/EventAsTextTile.js rename to src/components/views/login/VectorLoginFooter.js index ec644a4e..a5c6bbf8 100644 --- a/src/skins/vector/views/molecules/EventAsTextTile.js +++ b/src/components/views/login/VectorLoginFooter.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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/organisms/ViewSource.js b/src/components/views/login/VectorLoginHeader.js similarity index 73% rename from src/skins/vector/views/organisms/ViewSource.js rename to src/components/views/login/VectorLoginHeader.js index a00cfc85..0a3a8c51 100644 --- a/src/skins/vector/views/organisms/ViewSource.js +++ b/src/components/views/login/VectorLoginHeader.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,16 +19,16 @@ limitations under the License. var React = require('react'); module.exports = React.createClass({ - displayName: 'ViewSource', + displayName: 'VectorLoginHeader', + statics: { + replaces: 'LoginHeader', + }, render: function() { return ( -
-
-                    {JSON.stringify(this.props.mxEvent.event, null, 2)}
-                
+
+ vector
); } }); - diff --git a/src/skins/vector/views/molecules/DateSeparator.js b/src/components/views/messages/DateSeparator.js similarity index 97% rename from src/skins/vector/views/molecules/DateSeparator.js rename to src/components/views/messages/DateSeparator.js index 061ce66d..89cc44db 100644 --- a/src/skins/vector/views/molecules/DateSeparator.js +++ b/src/components/views/messages/DateSeparator.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/skins/vector/views/molecules/UnknownMessageTile.js b/src/components/views/messages/MessageTimestamp.js similarity index 71% rename from src/skins/vector/views/molecules/UnknownMessageTile.js rename to src/components/views/messages/MessageTimestamp.js index e8cd322a..609b5f4f 100644 --- a/src/skins/vector/views/molecules/UnknownMessageTile.js +++ b/src/components/views/messages/MessageTimestamp.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,16 +17,18 @@ limitations under the License. 'use strict'; var React = require('react'); +var DateUtils = require('matrix-react-sdk/lib/DateUtils'); module.exports = React.createClass({ - displayName: 'UnknownMessageTile', + displayName: 'MessageTimestamp', render: function() { - var content = this.props.mxEvent.getContent(); + var date = new Date(this.props.ts); return ( - - {content.body} + + { DateUtils.formatDate(date) } ); }, }); + diff --git a/src/skins/vector/views/molecules/MEmoteTile.js b/src/components/views/messages/SenderProfile.js similarity index 69% rename from src/skins/vector/views/molecules/MEmoteTile.js rename to src/components/views/messages/SenderProfile.js index de2d9365..9455cacb 100644 --- a/src/skins/vector/views/molecules/MEmoteTile.js +++ b/src/components/views/messages/SenderProfile.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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 89% rename from src/skins/vector/views/molecules/BottomLeftMenuTile.js rename to src/components/views/rooms/BottomLeftMenuTile.js index 2644769c..0db6bd92 100644 --- a/src/skins/vector/views/molecules/BottomLeftMenuTile.js +++ b/src/components/views/rooms/BottomLeftMenuTile.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -41,14 +41,14 @@ 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 = ; } return (
- +
{ label }
diff --git a/src/skins/vector/views/molecules/MessageContextMenu.js b/src/components/views/rooms/MessageContextMenu.js similarity index 73% rename from src/skins/vector/views/molecules/MessageContextMenu.js rename to src/components/views/rooms/MessageContextMenu.js index 249d9a34..a4631dfa 100644 --- a/src/skins/vector/views/molecules/MessageContextMenu.js +++ b/src/components/views/rooms/MessageContextMenu.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,30 +22,18 @@ 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("matrix-react-sdk/lib/Resend"); module.exports = React.createClass({ displayName: 'MessageContextMenu', onResendClick: function() { - MatrixClientPeg.get().resendEvent( - this.props.mxEvent, MatrixClientPeg.get().getRoom( - this.props.mxEvent.getRoomId() - ) - ).done(function() { - dis.dispatch({ - action: 'message_sent' - }); - }, function() { - dis.dispatch({ - action: 'message_send_failed' - }); - }); - dis.dispatch({action: 'message_resend_started'}); + Resend.resend(this.props.mxEvent); if (this.props.onFinished) this.props.onFinished(); }, onViewSourceClick: function() { - var ViewSource = sdk.getComponent('organisms.ViewSource'); + var ViewSource = sdk.getComponent('structures.ViewSource'); Modal.createDialog(ViewSource, { mxEvent: this.props.mxEvent }); @@ -58,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, { @@ -69,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 = (
- Delete + Redact
); } + + if (eventStatus === "queued" || eventStatus === "not_sent") { + cancelButton = ( +
+ Cancel Sending +
+ ); + } + viewSourceButton = (
View Source @@ -98,6 +103,7 @@ module.exports = React.createClass({
{resendButton} {redactButton} + {cancelButton} {viewSourceButton}
); diff --git a/src/components/views/rooms/RoomDNDView.js b/src/components/views/rooms/RoomDNDView.js new file mode 100644 index 00000000..e5c8fb33 --- /dev/null +++ b/src/components/views/rooms/RoomDNDView.js @@ -0,0 +1,205 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); +var DragSource = require('react-dnd').DragSource; +var DropTarget = require('react-dnd').DropTarget; + +var dis = require("matrix-react-sdk/lib/dispatcher"); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var sdk = require('matrix-react-sdk'); +var RoomTile = require('matrix-react-sdk/lib/components/views/rooms/RoomTile'); + +/** + * Specifies the drag source contract. + * Only `beginDrag` function is required. + */ +var roomTileSource = { + canDrag: function(props, monitor) { + return props.roomSubList.props.editable; + }, + + beginDrag: function (props) { + // Return the data describing the dragged item + var item = { + room: props.room, + originalList: props.roomSubList, + originalIndex: props.roomSubList.findRoomTile(props.room).index, + targetList: props.roomSubList, // at first target is same as original + // lastTargetRoom: null, + // lastYOffset: null, + // lastYDelta: null, + }; + + if (props.roomSubList.debug) console.log("roomTile beginDrag for " + item.room.roomId); + + // doing this 'correctly' with state causes react-dnd to break seemingly due to the state transitions + props.room._dragging = true; + + return item; + }, + + endDrag: function (props, monitor, component) { + var item = monitor.getItem(); + + if (props.roomSubList.debug) console.log("roomTile endDrag for " + item.room.roomId + " with didDrop=" + monitor.didDrop()); + + props.room._dragging = false; + if (monitor.didDrop()) { + if (props.roomSubList.debug) console.log("force updating component " + item.targetList.props.label); + item.targetList.forceUpdate(); // as we're not using state + } + + if (monitor.didDrop() && item.targetList.props.editable) { + // if we moved lists, remove the old tag + 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. + + //component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 }); + 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("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to remove tag " + item.originalList.props.tagName + " from room", + description: err.toString() + }); + }); + } + + var newOrder= {}; + if (item.targetList.props.order === 'manual') { + newOrder['order'] = item.targetList.calcManualOrderTagData(item.room); + } + + // if we moved lists or the ordering changed, add the new tag + if (item.targetList.props.tagName && (item.targetList !== item.originalList || newOrder)) { + //component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 }); + 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("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to add tag " + item.targetList.props.tagName + " to room", + description: err.toString() + }); + }); + } + } + else { + // cancel the drop and reset our original position + if (props.roomSubList.debug) console.log("cancelling drop & drag"); + props.roomSubList.moveRoomTile(item.room, item.originalIndex); + if (item.targetList && item.targetList !== item.originalList) { + item.targetList.removeRoomTile(item.room); + } + } + } +}; + +var roomTileTarget = { + canDrop: function() { + return false; + }, + + hover: function(props, monitor) { + var item = monitor.getItem(); + //var off = monitor.getClientOffset(); + // console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver()); + + //console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList); + + var switchedTarget = false; + if (item.targetList !== props.roomSubList) { + // we've switched target, so remove the tile from the previous target. + // n.b. the previous target might actually be the source list. + if (props.roomSubList.debug) console.log("switched target sublist"); + switchedTarget = true; + item.targetList.removeRoomTile(item.room); + item.targetList = props.roomSubList; + } + + if (!item.targetList.props.editable) return; + + if (item.targetList.props.order === 'manual') { + if (item.room.roomId !== props.room.roomId && props.room !== item.lastTargetRoom) { + // find the offset of the target tile in the list. + var roomTile = props.roomSubList.findRoomTile(props.room); + // shuffle the list to add our tile to that position. + props.roomSubList.moveRoomTile(item.room, roomTile.index); + } + + // stop us from flickering between our droptarget and the previous room. + // whenever the cursor changes direction we have to reset the flicker-damping. +/* + var yDelta = off.y - item.lastYOffset; + + if ((yDelta > 0 && item.lastYDelta < 0) || + (yDelta < 0 && item.lastYDelta > 0)) + { + // the cursor changed direction - forget our previous room + item.lastTargetRoom = null; + } + else { + // track the last room we were hovering over so we can stop + // bouncing back and forth if the droptarget is narrower than + // the other list items. The other way to do this would be + // to reduce the size of the hittarget on the list items, but + // can't see an easy way to do that. + item.lastTargetRoom = props.room; + } + + if (yDelta) item.lastYDelta = yDelta; + item.lastYOffset = off.y; +*/ + } + else if (switchedTarget) { + if (!props.roomSubList.findRoomTile(item.room).room) { + // add to the list in the right place + props.roomSubList.moveRoomTile(item.room, 0); + } + // we have to sort the list whatever to recalculate it + props.roomSubList.sortList(); + } + }, +}; + +// Export the wrapped version, inlining the 'collect' functions +// to more closely resemble the ES7 +module.exports = +DropTarget('RoomTile', roomTileTarget, function(connect, monitor) { + return { + // Call this function inside render() + // to let React DnD handle the drag events: + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + } +})( +DragSource('RoomTile', roomTileSource, function(connect, monitor) { + return { + // Call this function inside render() + // to let React DnD handle the drag events: + connectDragSource: connect.dragSource(), + // You can ask the monitor about the current drag state: + isDragging: monitor.isDragging() + }; +})(RoomTile)); + +module.exports.replaces = 'RoomTile'; diff --git a/src/skins/vector/views/atoms/EnableNotificationsButton.js b/src/components/views/rooms/RoomDropTarget.js similarity index 58% rename from src/skins/vector/views/atoms/EnableNotificationsButton.js rename to src/components/views/rooms/RoomDropTarget.js index edef9edc..789ba8fa 100644 --- a/src/skins/vector/views/atoms/EnableNotificationsButton.js +++ b/src/components/views/rooms/RoomDropTarget.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,20 +18,24 @@ limitations under the License. var React = require('react'); -var EnableNotificationsButtonController = require('matrix-react-sdk/lib/controllers/atoms/EnableNotificationsButton') - module.exports = React.createClass({ - displayName: 'EnableNotificationsButton', - mixins: [EnableNotificationsButtonController], + displayName: 'RoomDropTarget', render: function() { - if (this.enabled()) { + if (this.props.placeholder) { return ( - +
+
); - } else { + } + else { return ( - +
+
+
+ { this.props.label } +
+
); } } diff --git a/src/skins/vector/views/molecules/RoomTooltip.js b/src/components/views/rooms/RoomTooltip.js similarity index 89% rename from src/skins/vector/views/molecules/RoomTooltip.js rename to src/components/views/rooms/RoomTooltip.js index 82e3e744..e3cbc6ec 100644 --- a/src/skins/vector/views/molecules/RoomTooltip.js +++ b/src/components/views/rooms/RoomTooltip.js @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); +var ReactDOM = require('react-dom'); var dis = require('matrix-react-sdk/lib/dispatcher'); @@ -24,21 +25,21 @@ module.exports = React.createClass({ displayName: 'RoomTooltip', componentDidMount: function() { + var tooltip = ReactDOM.findDOMNode(this); if (!this.props.bottom) { // tell the roomlist about us so it can position us dis.dispatch({ action: 'view_tooltip', - tooltip: this.getDOMNode(), + tooltip: tooltip, }); } else { - var tooltip = this.getDOMNode(); tooltip.style.top = tooltip.parentElement.getBoundingClientRect().top + "px"; tooltip.style.display = "block"; } }, - componentDidUnmount: function() { + componentWillUnmount: function() { if (!this.props.bottom) { dis.dispatch({ action: 'view_tooltip', diff --git a/src/components/views/rooms/SearchBar.js b/src/components/views/rooms/SearchBar.js new file mode 100644 index 00000000..955b4c51 --- /dev/null +++ b/src/components/views/rooms/SearchBar.js @@ -0,0 +1,69 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +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', + + getInitialState: function() { + return ({ + scope: 'Room' + }); + }, + + onThisRoomClick: function() { + this.setState({ scope: 'Room' }); + }, + + onAllRoomsClick: function() { + this.setState({ scope: 'All' }); + }, + + onSearchChange: function(e) { + if (e.keyCode === 13) { // on enter... + 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 ( +
+ +
Search
+
This Room
+
All Rooms
+ +
+ ); + } +}); diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js new file mode 100644 index 00000000..4c880760 --- /dev/null +++ b/src/components/views/settings/Notifications.js @@ -0,0 +1,1115 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; +var React = require('react'); +var q = require("q"); +var sdk = require('matrix-react-sdk'); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore'); +var Modal = require('matrix-react-sdk/lib/Modal'); + +/** + * Enum for state of a push rule as defined by the Vector UI. + * @readonly + * @enum {string} + */ +var PushRuleVectorState = { + /** The user will receive push notification for this rule */ + ON: "on", + /** The user will receive push notification for this rule with sound and + highlight if this is legitimate */ + LOUD: "loud", + /** The push rule is disabled */ + OFF: "off" +}; + +/** + * The descriptions of rules managed by the Vector UI. + * Each rule is described so that if the server does not have it in its default + * rules or if the user wants to use actions ('PushRuleVectorState') that are + * different from the hs one, the code will create a new rule that will override + * the hs one. + */ +var VectorPushRulesDefinitions = { + + // Messages containing user's display name + // (skip contains_user_name which is too geeky) + "im.vector.rule.contains_display_name": { + kind: "underride", + hsDefaultRuleId: ".m.rule.contains_display_name", + description: "Messages containing my name", + conditions: [{ + "kind": "contains_display_name" + }], + vectorStateToActions: { // The actions for each vector state + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak":"highlight" + } + ] + }, + vectorStateToHsDefaultRuleEnabled: { // If it exists, the hs default push rule enabled expected value for each vector state + on: undefined, // ON (and its actions) does not corresponds to the default hs push rule, so NA + loud: true, // LOUD corresponds to the default rule when its enabled value is true + off: false // OFF corresponds to the default rule when its enabled value is false + }, + }, + + // Messages just sent to the user in a 1:1 room + "im.vector.rule.room_one_to_one": { + kind: "underride", + hsDefaultRuleId: ".m.rule.room_one_to_one", + description: "Messages in one-to-one chats", + conditions: [{ + "is": "2", + "kind": "room_member_count" + }], + vectorStateToActions: { + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ], + off: [ + "dont_notify" + ] + }, + vectorStateToHsDefaultRuleEnabled: { + on: undefined, + loud: true, + off: undefined + } + }, + + // Messages just sent to a group chat room + // 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined + // By opposition, all other room messages are from group chat rooms. + "im.vector.rule.room_message": { + kind: "underride", + description: "Messages in group chats", + conditions: [{ + "pattern": "m.room.message", + "kind": "event_match", + "key": "type" + }], + hsDefaultRuleId: ".m.rule.message", + vectorStateToActions: { + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ], + off: [ + "dont_notify" + ] + }, + vectorStateToHsDefaultRuleEnabled: { + on: true, + loud: undefined, + off: undefined + } + }, + + // Invitation for the user + "im.vector.rule.invite_for_me": { + kind: "underride", + hsDefaultRuleId: ".m.rule.invite_for_me", + description: "When I'm invited to a room", + conditions: [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + }, + { + "key": "content.membership", + "kind": "event_match", + "pattern": "invite" + }, + { + "key": "state_key", + "kind": "event_match", + "pattern": "" // It is updated at runtime the user id + } + ], + vectorStateToActions: { + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ] + }, + vectorStateToHsDefaultRuleEnabled: { + on: undefined, + loud: true, + off: false + } + }, + + // When people join or leave a room + /*"im.vector.rule.member_event": { + hsDefaultRuleId: ".m.rule.member_event", + description: "When people join or leave a room", + conditions: [{ + "pattern": "m.room.member", + "kind": "event_match", + "key": "type" + }], + vectorStateToActions: { + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ] + }, + vectorStateToHsDefaultRuleEnabled: { + on: true, + loud: undefined, + off: false + } + },*/ + + // Incoming call + "im.vector.rule.call": { + kind: "underride", + hsDefaultRuleId: ".m.rule.call", + description: "Call invitation", + conditions: [{ + "pattern": "m.room.member", + "kind": "event_match", + "key": "type" + }], + vectorStateToActions: { + on: [ + "notify" + ], + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "ring" + } + ], + }, + vectorStateToHsDefaultRuleEnabled: { + on: undefined, + loud: true, + off: false + } + }, + + // Notifications from bots + "im.vector.rule.notices": { + kind: "override", + hsDefaultRuleId: ".m.rule.suppress_notices", + description: "Messages sent by bot", + conditions: [{ + "kind": "event_match", + "key": "content.msgtype", + "pattern": "m.notice" + }], + vectorStateToActions: { + on: undefined, // ON for vector UI means that the .m.rule.suppress_notices rule is disabled. + loud: [ + "notify", + { + "set_tweak": "sound", + "value": "ring" + } + ], + off: [ + "dont_notify" + ] + }, + vectorStateToHsDefaultRuleEnabled: { + on: false, // .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI + loud: undefined, + off: true + } + } +}; + +module.exports = React.createClass({ + displayName: 'Notififications', + + phases: { + LOADING: "LOADING", // The component is loading or sending data to the hs + DISPLAY: "DISPLAY", // The component is ready and display data + ERROR: "ERROR" // There was an error + }, + + getInitialState: function() { + return { + phase: this.phases.LOADING, + masterPushRule: undefined, // The master rule ('.m.rule.master') + vectorPushRules: [], // HS default push rules displayed in Vector UI + vectorContentRules: { // Keyword push rules displayed in Vector UI + vectorState: PushRuleVectorState.ON, + rules: [] + }, + externalPushRules: [], // Push rules (except content rule) that have been defined outside Vector UI + externalContentRules: [] // Keyword push rules that have been defined outside Vector UI + }; + }, + + componentWillMount: function() { + // Finalise the vector definitions + VectorPushRulesDefinitions["im.vector.rule.invite_for_me"].conditions[2].pattern = MatrixClientPeg.get().credentials.userId; + + this._refreshFromServer(); + }, + + onEnableNotificationsChange: function(event) { + var self = this; + this.setState({ + phase: this.phases.LOADING + }); + + MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !event.target.checked).done(function() { + self._refreshFromServer(); + }); + }, + + onEnableDesktopNotificationsChange: function(event) { + UserSettingsStore.setEnableNotifications(event.target.checked); + }, + + onNotifStateButtonClicked: function(event) { + var vectorRuleId = event.target.className.split("-")[0]; + var newPushRuleVectorState = event.target.className.split("-")[1]; + + if ("_keywords" === vectorRuleId) { + this._setKeywordsPushRuleVectorState(newPushRuleVectorState) + } + else { + var rule = this.getRule(vectorRuleId); + if (rule) { + this._setPushRuleVectorState(rule, newPushRuleVectorState); + } + } + }, + + onKeywordsClicked: function(event) { + var self = this; + + // Compute the keywords list to display + var keywords = []; + for (var i in this.state.vectorContentRules.rules) { + var rule = this.state.vectorContentRules.rules[i]; + keywords.push(rule.pattern); + } + if (keywords.length) { + // As keeping the order of per-word push rules hs side is a bit tricky to code, + // display the keywords in alphabetical order to the user + keywords.sort(); + + keywords = keywords.join(", "); + } + else { + keywords = ""; + } + + var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog"); + Modal.createDialog(TextInputDialog, { + title: "Keywords", + description: "Enter keywords separated by a comma:", + value: keywords, + onFinished: function onFinished(should_leave, newValue) { + + if (should_leave && newValue !== keywords) { + var newKeywords = newValue.split(','); + for (var i in newKeywords) { + newKeywords[i] = newKeywords[i].trim(); + } + + // Remove duplicates and empty + newKeywords = newKeywords.reduce(function(array, keyword){ + if (keyword !== "" && array.indexOf(keyword) < 0) { + array.push(keyword); + } + return array; + },[]); + + self._setKeywords(newKeywords); + } + } + }); + }, + + getRule: function(vectorRuleId) { + for (var i in this.state.vectorPushRules) { + var rule = this.state.vectorPushRules[i]; + if (rule.vectorRuleId === vectorRuleId) { + return rule; + } + } + }, + + _actionsFor: function(pushRuleVectorState) { + if (pushRuleVectorState === PushRuleVectorState.ON) { + return ['notify']; + } + else if (pushRuleVectorState === PushRuleVectorState.LOUD) { + return ['notify', + {'set_tweak': 'sound', 'value': 'default'}, + {'set_tweak': 'highlight', 'value': 'true'} + ];; + } + }, + + // Determine whether a content rule is in the PushRuleVectorState.ON category or in PushRuleVectorState.LOUD + // regardless of its enabled state. Returns undefined if it does not match these categories. + _contentRuleVectorStateKind: function(rule) { + var stateKind; + + // Count tweaks to determine if it is a ON or LOUD rule + var tweaks = 0; + for (var j in rule.actions) { + var action = rule.actions[j]; + if (action.set_tweak === 'sound' || + (action.set_tweak === 'highlight' && action.value)) { + tweaks++; + } + } + switch (tweaks) { + case 0: + stateKind = PushRuleVectorState.ON; + break; + case 2: + stateKind = PushRuleVectorState.LOUD; + break; + } + return stateKind; + }, + + _setPushRuleVectorState: function(rule, newPushRuleVectorState) { + if (rule && rule.vectorState !== newPushRuleVectorState) { + + this.setState({ + phase: this.phases.LOADING + }); + + var self = this; + var cli = MatrixClientPeg.get(); + var deferreds = []; + var ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId]; + + if (rule.rule) { + if (undefined !== ruleDefinition.vectorStateToHsDefaultRuleEnabled[newPushRuleVectorState] && rule.hsDefaultRule) { + // The new state corresponds to the default hs rule + // Enable or disable it according to the rule definition + deferreds.push(cli.setPushRuleEnabled('global', rule.hsDefaultRule.kind, ruleDefinition.hsDefaultRuleId, + ruleDefinition.vectorStateToHsDefaultRuleEnabled[newPushRuleVectorState])); + + // Remove the vector rule if any + if (!rule.isHSDefaultRule) { + deferreds.push(cli.deletePushRule('global', rule.rule.kind, rule.rule.rule_id)) + } + } + else { + // The new state (and its implied actions) does not correspond to a default hs rule + // or the HS does not expose this default rule. + if (rule.isHSDefaultRule) { + // Create a new rule that will override the default one + deferreds.push(this._addOverridingVectorPushRule(rule.vectorRuleId, newPushRuleVectorState)); + } + else { + // Change the actions of the existing overriding Vector rule + deferreds.push(this._updatePushRuleActions(rule.rule, ruleDefinition.vectorStateToActions[newPushRuleVectorState])); + } + } + } + else { + // This is a Vector rule which does not exist yet server side + // Create it + deferreds.push(this._addOverridingVectorPushRule(rule.vectorRuleId, newPushRuleVectorState)); + } + + q.all(deferreds).done(function() { + self._refreshFromServer(); + }, function(error) { + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Can't change settings", + description: error.toString(), + onFinished: self._refreshFromServer + }); + }); + } + }, + + _setKeywordsPushRuleVectorState: function(newPushRuleVectorState) { + // Is there really a change? + if (this.state.vectorContentRules.vectorState === newPushRuleVectorState + || this.state.vectorContentRules.rules.length === 0) { + return; + } + + var self = this; + var cli = MatrixClientPeg.get(); + + this.setState({ + phase: this.phases.LOADING + }); + + // Update all rules in self.state.vectorContentRules + var deferreds = []; + for (var i in this.state.vectorContentRules.rules) { + var rule = this.state.vectorContentRules.rules[i]; + + var enabled, actions; + switch (newPushRuleVectorState) { + case PushRuleVectorState.ON: + if (rule.actions.length !== 1) { + actions = this._actionsFor(PushRuleVectorState.ON); + } + + if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) { + enabled = true; + } + break; + + case PushRuleVectorState.LOUD: + if (rule.actions.length !== 3) { + actions = this._actionsFor(PushRuleVectorState.LOUD); + } + + if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) { + enabled = true; + } + break; + + case PushRuleVectorState.OFF: + enabled = false; + break; + } + + if (actions) { + // Note that the workaround in _updatePushRuleActions will automatically + // enable the rule + deferreds.push(this._updatePushRuleActions(rule, actions, enabled)); + } + else if (enabled != undefined) { + deferreds.push(cli.setPushRuleEnabled('global', rule.kind, rule.rule_id, enabled)); + } + } + + q.all(deferreds).done(function(resps) { + self._refreshFromServer(); + }, function(error) { + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Can't update user notification settings", + description: error.toString(), + onFinished: self._refreshFromServer + }); + }); + }, + + _setKeywords: function(newKeywords) { + this.setState({ + phase: this.phases.LOADING + }); + + var self = this; + var cli = MatrixClientPeg.get(); + var removeDeferreds = []; + + // Remove per-word push rules of keywords that are no more in the list + var vectorContentRulesPatterns = []; + for (var i in self.state.vectorContentRules.rules) { + var rule = self.state.vectorContentRules.rules[i]; + + vectorContentRulesPatterns.push(rule.pattern); + + if (newKeywords.indexOf(rule.pattern) < 0) { + removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id)); + } + } + + // If the keyword is part of `externalContentRules`, remove the rule + // before recreating it in the right Vector path + for (var i in self.state.externalContentRules) { + var rule = self.state.externalContentRules[i]; + + if (newKeywords.indexOf(rule.pattern) >= 0) { + removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id)); + } + } + + var onError = function(error) { + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Can't update keywords", + description: error.toString(), + onFinished: self._refreshFromServer + }); + } + + // Then, add the new ones + q.all(removeDeferreds).done(function(resps) { + var deferreds = []; + + var pushRuleVectorStateKind = self.state.vectorContentRules.vectorState; + if (pushRuleVectorStateKind === PushRuleVectorState.OFF) { + // When the current global keywords rule is OFF, we need to look at + // the flavor of rules in 'vectorContentRules' to apply the same actions + // when creating the new rule. + // Thus, this new rule will join the 'vectorContentRules' set. + if (self.state.vectorContentRules.rules.length) { + pushRuleVectorStateKind = self._contentRuleVectorStateKind(self.state.vectorContentRules.rules[0]); + } + else { + // ON is default + pushRuleVectorStateKind = PushRuleVectorState.ON; + } + } + + for (var i in newKeywords) { + var keyword = newKeywords[i]; + + if (vectorContentRulesPatterns.indexOf(keyword) < 0) { + if (self.state.vectorContentRules.vectorState !== PushRuleVectorState.OFF) { + deferreds.push(cli.addPushRule + ('global', 'content', keyword, { + actions: self._actionsFor(pushRuleVectorStateKind), + pattern: keyword + })); + } + else { + deferreds.push(self._addDisabledPushRule('global', 'content', keyword, { + actions: self._actionsFor(pushRuleVectorStateKind), + pattern: keyword + })); + } + } + } + + q.all(deferreds).done(function(resps) { + self._refreshFromServer(); + }, onError); + }, onError); + }, + + // Create a push rule but disabled + _addDisabledPushRule: function(scope, kind, ruleId, body) { + var cli = MatrixClientPeg.get(); + var deferred = q.defer(); + + cli.addPushRule(scope, kind, ruleId, body).done(function() { + cli.setPushRuleEnabled(scope, kind, ruleId, false).done(function() { + deferred.resolve(); + }, function(err) { + deferred.reject(err); + }); + }, function(err) { + deferred.reject(err); + }); + + return deferred.promise; + }, + + // Add a push rule server side according to the 'VectorPushRulesDefinitions' spec + _addOverridingVectorPushRule: function(vectorRuleId, vectorState) { + var self = this; + + // Create the rule as predefined + var ruleDefinition = VectorPushRulesDefinitions[vectorRuleId]; + var body = { + conditions: ruleDefinition.conditions, + actions: ruleDefinition.vectorStateToActions[vectorState] + } + + return MatrixClientPeg.get().addPushRule('global', ruleDefinition.kind, vectorRuleId, body); + }, + + _refreshFromServer: function() { + var self = this; + MatrixClientPeg.get().getPushRules().done(function(rulesets) { + MatrixClientPeg.get().pushRules = rulesets; + + // Get homeserver default rules and triage them by categories + var rule_categories = { + // The master rule (all notifications disabling) + '.m.rule.master': 'master', + + // The default push rules displayed by Vector UI + // XXX: .m.rule.contains_user_name is not managed (not a fancy rule for Vector?) + '.m.rule.contains_display_name': 'vector', + '.m.rule.room_one_to_one': 'vector', + '.m.rule.message': 'vector', + '.m.rule.invite_for_me': 'vector', + //'.m.rule.member_event': 'vector', + '.m.rule.call': 'vector', + '.m.rule.suppress_notices': 'vector' + + // Others go to others + }; + + // HS default rules + var defaultRules = {master: [], vector: {}, others: []}; + // Push rules defined py Vector to override hs default rules + var vectorOverridingRules = {}; + // Content/keyword rules + var contentRules = {on: [], on_but_disabled:[], loud: [], loud_but_disabled: [], other: []}; + + for (var kind in rulesets.global) { + for (var i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) { + var r = rulesets.global[kind][i]; + var cat = rule_categories[r.rule_id]; + r.kind = kind; + if (r.rule_id[0] === '.') { + if (cat) { + if (cat === 'vector') { + // Remove disabled, useless actions + r.actions = r.actions.reduce(function(array, action){ + if (action.value !== false) { + array.push(action); + } + return array; + },[]); + + defaultRules.vector[r.rule_id] = r; + } + else { + defaultRules[cat].push(r); + } + } + else { + defaultRules['others'].push(r); + } + } + else if (r.rule_id.startsWith('im.vector')) { + vectorOverridingRules[r.rule_id] = r; + } + else if (kind === 'content') { + switch (self._contentRuleVectorStateKind(r)) { + case PushRuleVectorState.ON: + if (r.enabled) { + contentRules.on.push(r); + } + else { + contentRules.on_but_disabled.push(r); + } + break; + case PushRuleVectorState.LOUD: + if (r.enabled) { + contentRules.loud.push(r); + } + else { + contentRules.loud_but_disabled.push(r); + } + break; + default: + contentRules.other.push(r); + break; + } + } + } + } + + // Decide which content rules to display in Vector UI. + // Vector displays a single global rule for a list of keywords + // whereas Matrix has a push rule per keyword. + // Vector can set the unique rule in ON, LOUD or OFF state. + // Matrix has enabled/disabled plus a combination of (highlight, sound) tweaks. + + // The code below determines which set of user's content push rules can be + // displayed by the vector UI. + // Push rules that does not fit, ie defined by another Matrix client, ends + // in self.state.externalContentRules. + // There is priority in the determination of which set will be the displayed one. + // The set with rules that have LOUD tweaks is the first choice. Then, the ones + // with ON tweaks (no tweaks). + if (contentRules.loud.length) { + self.state.vectorContentRules = { + vectorState: PushRuleVectorState.LOUD, + rules: contentRules.loud + } + self.state.externalContentRules = [].concat(contentRules.loud_but_disabled, contentRules.on, contentRules.on_but_disabled, contentRules.other); + } + else if (contentRules.loud_but_disabled.length) { + self.state.vectorContentRules = { + vectorState: PushRuleVectorState.OFF, + rules: contentRules.loud_but_disabled + } + self.state.externalContentRules = [].concat(contentRules.on, contentRules.on_but_disabled, contentRules.other); + } + else if (contentRules.on.length) { + self.state.vectorContentRules = { + vectorState: PushRuleVectorState.ON, + rules: contentRules.on + } + self.state.externalContentRules = [].concat(contentRules.on_but_disabled, contentRules.other); + } + else if (contentRules.on_but_disabled.length) { + self.state.vectorContentRules = { + vectorState: PushRuleVectorState.OFF, + rules: contentRules.on_but_disabled + } + self.state.externalContentRules = contentRules.other; + } + else { + self.state.externalContentRules = contentRules.other; + } + + // Get the master rule if any defined by the hs + if (defaultRules.master.length > 0) { + self.state.masterPushRule = defaultRules.master[0]; + } + + // Build the rules displayed in the Vector UI matrix table + self.state.vectorPushRules = []; + + var vectorRuleIds = [ + 'im.vector.rule.contains_display_name', + '_keywords', + 'im.vector.rule.room_one_to_one', + 'im.vector.rule.room_message', + 'im.vector.rule.invite_for_me', + //'im.vector.rule.member_event', + 'im.vector.rule.call', + 'im.vector.rule.notices' + ]; + for (var i in vectorRuleIds) { + var vectorRuleId = vectorRuleIds[i]; + var ruleDefinition = VectorPushRulesDefinitions[vectorRuleId]; + + if (vectorRuleId === '_keywords') { + // keywords needs a special handling + // For Vector UI, this is a single global push rule but translated in Matrix, + // it corresponds to all content push rules (stored in self.state.vectorContentRule) + self.state.vectorPushRules.push({ + "vectorRuleId": "_keywords", + "description" : (Messages containing keywords), + "vectorState": self.state.vectorContentRules.vectorState + }); + } + else { + var rule = vectorOverridingRules[vectorRuleId]; + var isHSDefaultRule = false; + if (!rule) { + // If the rule is not defined, look at the hs default one + rule = defaultRules.vector[ruleDefinition.hsDefaultRuleId]; + isHSDefaultRule = true; + } + + // Translate the rule actions and its enabled value into vector state + var vectorState; + if (rule) { + for (var stateKey in PushRuleVectorState) { + var state = PushRuleVectorState[stateKey]; + var vectorStateToActions = ruleDefinition.vectorStateToActions[state]; + + if (!vectorStateToActions) { + // No defined actions means that this vector state expects a disabled default hs rule + if (isHSDefaultRule && rule.enabled === ruleDefinition.vectorStateToHsDefaultRuleEnabled[state]) { + vectorState = state; + break; + } + } + else { + // The actions must match to the ones expected by vector state + if (JSON.stringify(rule.actions) === JSON.stringify(vectorStateToActions)) { + if (isHSDefaultRule) { + // In the case of a default hs push rule, the enabled value must also match + if (rule.enabled === ruleDefinition.vectorStateToHsDefaultRuleEnabled[state]) { + vectorState = state; + break; + } + } + else { + vectorState = state; + break; + } + } + } + } + + if (!vectorState) { + console.error("Cannot translate rule actions into Vector rule state. Rule: " + rule); + vectorState = PushRuleVectorState.OFF; + } + } + else { + vectorState = PushRuleVectorState.OFF; + } + + self.state.vectorPushRules.push({ + "vectorRuleId": vectorRuleId, + "description" : ruleDefinition.description, + "rule": rule, + "vectorState": vectorState, + "isHSDefaultRule": isHSDefaultRule, + "hsDefaultRule": defaultRules.vector[ruleDefinition.hsDefaultRuleId] + }); + } + } + + // Build the rules not managed by Vector UI + var otherRulesDescriptions = { + '.m.rule.message': "Notify for all other messages/rooms", + '.m.rule.fallback': "Notify me for anything else" + }; + + self.state.externalPushRules = []; + for (var i in defaultRules.others) { + var rule = defaultRules.others[i]; + var ruleDescription = otherRulesDescriptions[rule.rule_id]; + + // Show enabled default rules that was modified by the user + if (ruleDescription && rule.enabled && !rule.default) { + rule.description = ruleDescription; + self.state.externalPushRules.push(rule); + } + } + + self.setState({ + phase: self.phases.DISPLAY + }); + }); + }, + + _updatePushRuleActions: function(rule, actions, enabled) { + // Workaround for SYN-590 : Push rule update fails + // Remove the rule and recreate it with the new actions + var cli = MatrixClientPeg.get(); + var deferred = q.defer(); + + cli.deletePushRule('global', rule.kind, rule.rule_id).done(function() { + cli.addPushRule('global', rule.kind, rule.rule_id, { + conditions: rule.conditions, + actions: actions, + pattern: rule.pattern + }).done(function() { + + // Then, if requested, enabled or disabled the rule + if (undefined != enabled) { + cli.setPushRuleEnabled('global', rule.kind, rule.rule_id, enabled).done(function() { + deferred.resolve(); + }, function(err) { + deferred.reject(err); + }); + } + else { + deferred.resolve(); + } + }, function(err) { + deferred.reject(err); + }); + }, function(err) { + deferred.reject(err); + }); + + return deferred.promise; + }, + + renderNotifRulesTableRow: function(title, className, pushRuleVectorState) { + return ( + + + {title} + + + + + + + + + + + + + + + ); + }, + + renderNotifRulesTableRows: function() { + var rows = []; + for (var i in this.state.vectorPushRules) { + var rule = this.state.vectorPushRules[i]; + rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState)); + } + return rows; + }, + + render: function() { + var self = this; + + if (this.state.phase === this.phases.LOADING) { + var Loader = sdk.getComponent("elements.Spinner"); + return ( +
+ +
+ ); + } + + if (this.state.masterPushRule) { + var masterPushRuleDiv = ( +
+
+ +
+
+ +
+
+ ); + } + + // When enabled, the master rule inhibits all existing rules + // So do not show all notification settings + if (this.state.masterPushRule.enabled) { + return ( +
+ {masterPushRuleDiv} + +
+ All notifications are currently disabled for all devices. +
+
+ ); + } + + // Build external push rules + var externalRules = []; + for (var i in this.state.externalPushRules) { + var rule = this.state.externalPushRules[i]; + externalRules.push(
  • { rule.description }
  • ); + } + + // Show keywords not displayed by the vector UI as a single external push rule + var externalKeyWords = []; + for (var i in this.state.externalContentRules) { + var rule = this.state.externalContentRules[i]; + externalKeyWords.push(rule.pattern); + } + if (externalKeyWords.length) { + externalKeyWords = externalKeyWords.join(", "); + externalRules.push(
  • Notifications on the following keywords follow rules which can’t be displayed here: { externalKeyWords }
  • ); + } + + var advancedSettings; + if (externalRules.length) { + advancedSettings = ( +
    +

    Advanced notifications settings

    + There are advanced notifications which are not shown here.
    + You might have configured them in another client than Vector. You cannot tune them in Vector but they still apply. +
      + { externalRules } +
    +
    + ); + } + + return ( +
    + + {masterPushRuleDiv} + +
    + +
    +
    + +
    +
    + +
    +
    + +

    General use

    + +
    + + + + + + + + + + + + { this.renderNotifRulesTableRows() } + + +
    OnLoudOff
    +
    + + { advancedSettings } + +
    + +
    + ); + } +}); diff --git a/src/controllers/molecules/voip/CallView.js b/src/controllers/molecules/voip/CallView.js deleted file mode 100644 index ab712148..00000000 --- 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 151a6ca2..00000000 --- a/src/controllers/organisms/RoomList.js +++ /dev/null @@ -1,194 +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 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 CallHandler = require("matrix-react-sdk/lib/CallHandler"); - -var HIDE_CONFERENCE_CHANS = true; - -module.exports = { - 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("RoomState.events", this.onRoomStateEvents); - cli.on("RoomMember.name", this.onRoomMemberName); - - var rooms = this.getRoomList(); - this.setState({ - roomList: rooms, - activityMap: {} - }); - }, - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - }, - - onAction: function(payload) { - switch (payload.action) { - // listen for call state changes to prod the render method, which - // may hide the global CallView if the call it is tracking is dead - case 'call_state': - this._recheckCallElement(this.props.selectedRoom); - break; - 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._recheckCallElement(newProps.selectedRoom); - this.setState({ - activityMap: this.state.activityMap - }); - }, - - onRoom: function(room) { - this.refreshRoomList(); - }, - - onRoomTimeline: function(ev, room, toStartOfTimeline) { - if (toStartOfTimeline) return; - - var newState = { - roomList: this.getRoomList() - }; - 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(); - }, - - onRoomStateEvents: function(ev, state) { - setTimeout(this.refreshRoomList, 0); - }, - - onRoomMemberName: function(ev, member) { - setTimeout(this.refreshRoomList, 0); - }, - - - refreshRoomList: function() { - var rooms = this.getRoomList(); - this.setState({ - roomList: rooms - }); - }, - - getRoomList: function() { - return RoomListSorter.mostRecentActivityFirst( - MatrixClientPeg.get().getRooms().filter(function(room) { - var me = room.getMember(MatrixClientPeg.get().credentials.userId); - var shouldShowRoom = ( - me && (me.membership == "join" || me.membership == "invite") - ); - // 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; - } - } - } - return shouldShowRoom; - }) - ); - }, - - _recheckCallElement: function(selectedRoomId) { - // if we aren't viewing a room with an ongoing call, but there is an - // active call, show the call element - we need to do this to make - // audio/video not crap out - var activeCall = CallHandler.getAnyActiveCall(); - var callForRoom = CallHandler.getCallForRoom(selectedRoomId); - var showCall = (activeCall && !callForRoom); - this.setState({ - show_call_element: showCall - }); - }, - - _repositionTooltip: function(e) { - if (this.tooltip && this.tooltip.parentElement) { - var scroll = this.getDOMNode(); - this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.scrollTop) + "px"; - } - }, - - makeRoomTiles: function() { - var self = this; - var RoomTile = sdk.getComponent("molecules.RoomTile"); - return this.state.roomList.map(function(room) { - var selected = room.roomId == self.props.selectedRoom; - return ( - - ); - }); - } -}; diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js deleted file mode 100644 index 21027cbf..00000000 --- a/src/controllers/organisms/RoomView.js +++ /dev/null @@ -1,503 +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 React = require("react"); -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 dis = require("matrix-react-sdk/lib/dispatcher"); - -var PAGINATE_SIZE = 20; -var INITIAL_SIZE = 20; - -module.exports = { - getInitialState: function() { - return { - room: this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null, - messageCap: INITIAL_SIZE, - editingRoomSettings: false, - uploadingRoomSettings: false, - numUnreadMessages: 0, - draggingFile: false, - } - }, - - 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); - this.atBottom = true; - }, - - componentWillUnmount: function() { - if (this.refs.messageWrapper) { - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - messageWrapper.removeEventListener('drop', this.onDrop); - messageWrapper.removeEventListener('dragover', this.onDragOver); - messageWrapper.removeEventListener('dragleave', this.onDragLeaveOrEnd); - messageWrapper.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); - } - }, - - onAction: function(payload) { - switch (payload.action) { - case 'message_send_failed': - case 'message_sent': - 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 messageWrapper = this.refs.messageWrapper; - if (messageWrapper) { - messageWrapper = messageWrapper.getDOMNode(); - messageWrapper.scrollTop = messageWrapper.scrollHeight; - } - } - - // possibly remove the conf call notification if we're now in - // the conf - this._updateConfCallNotification(); - break; - } - }, - - // 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 pagingating: 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; - - if (this.refs.messageWrapper) { - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - this.atBottom = ( - messageWrapper.scrollHeight - messageWrapper.scrollTop <= - (messageWrapper.clientHeight + 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(); - }, - - _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.messageWrapper) { - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - - messageWrapper.addEventListener('drop', this.onDrop); - messageWrapper.addEventListener('dragover', this.onDragOver); - messageWrapper.addEventListener('dragleave', this.onDragLeaveOrEnd); - messageWrapper.addEventListener('dragend', this.onDragLeaveOrEnd); - - messageWrapper.scrollTop = messageWrapper.scrollHeight; - - this.fillSpace(); - } - - this._updateConfCallNotification(); - }, - - componentDidUpdate: function() { - if (!this.refs.messageWrapper) return; - - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - - if (this.state.paginating && !this.waiting_for_paginate) { - var heightGained = messageWrapper.scrollHeight - this.oldScrollHeight; - messageWrapper.scrollTop += heightGained; - this.oldScrollHeight = undefined; - if (!this.fillSpace()) { - this.setState({paginating: false}); - } - } else if (this.atBottom) { - messageWrapper.scrollTop = messageWrapper.scrollHeight; - if (this.state.numUnreadMessages !== 0) { - this.setState({numUnreadMessages: 0}); - } - } - }, - - fillSpace: function() { - if (!this.refs.messageWrapper) return; - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - if (messageWrapper.scrollTop < messageWrapper.clientHeight && this.state.room.oldState.paginationToken) { - this.setState({paginating: true}); - - this.oldScrollHeight = messageWrapper.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; - }, - - 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.messageWrapper) { - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - var wasAtBottom = this.atBottom; - this.atBottom = messageWrapper.scrollHeight - messageWrapper.scrollTop <= messageWrapper.clientHeight; - 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() { - // display error message - }); - }, - - getWhoIsTypingString: function() { - return WhoIsTyping.whoIsTypingString(this.state.room); - }, - - getEventTiles: function() { - var DateSeparator = sdk.getComponent('molecules.DateSeparator'); - - var ret = []; - var count = 0; - - var EventTile = sdk.getComponent('molecules.EventTile'); - - 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.supportsEventType(mxEv.getType())) { - 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 5f88d590..00000000 --- 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 93% rename from src/skins/vector/header rename to src/header index fd88ee27..060709b8 100644 --- a/src/skins/vector/header +++ b/src/header @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/skins/vector/css/common.css b/src/skins/vector/css/common.css index 93012c0f..53ab57fe 100644 --- a/src/skins/vector/css/common.css +++ b/src/skins/vector/css/common.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,8 +22,13 @@ html { } body { - font-family: 'Lato', 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; @@ -34,9 +39,9 @@ div.error { } h2 { - color: #80cef4; + color: #454545; font-weight: 400; - font-size: 20px; + font-size: 18px; margin-top: 16px; margin-bottom: 16px; } @@ -44,7 +49,21 @@ h2 { a:hover, a:link, a:visited { - color: #80CEF4; + color: #76cfa6; +} + +input[type=text]:focus, 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. + */ +.gm-scrollbar-container .gm-scroll-view { + position: absolute; } .mx_ContextualMenu_background { @@ -58,7 +77,7 @@ a:visited { } .mx_ContextualMenu { - border: 1px solid #a9dbf4; + border: 1px solid #a4a4a4; border-radius: 8px; background-color: #fff; color: #747474; @@ -91,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: 2000; -} - .mx_Dialog_wrapper { position: fixed; + z-index: 4000; top: 0; left: 0; width: 100%; @@ -124,18 +133,36 @@ a:visited { background-color: #fff; color: #747474; text-align: center; - z-index: 2010; + z-index: 4010; font-weight: 300; - font-size: 16px; + font-size: 15px; position: relative; border-radius: 8px; - max-width: 75%; + max-width: 80%; } -.mx_ImageView { - margin: 6px; - /* hack: flexbox bug? */ - margin-bottom: 4px; +.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; +} + +.mx_Dialog_lightbox .mx_Dialog { + border-radius: 0px; + background-color: transparent; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + pointer-events: none; } .mx_Dialog_content { @@ -146,26 +173,38 @@ a:visited { padding-bottom: 24px; } -.mx_Dialog button { +.mx_Dialog button, .mx_Dialog input[type="submit"] { border: 0px; height: 36px; border-radius: 36px; font-weight: 400; - font-size: 16px; + font-size: 15px; color: #fff; - background-color: #80cef4; + background-color: #76cfa6; margin-left: 8px; margin-right: 8px; padding-left: 1em; padding-right: 1em; } -.mx_ErrorDialogTitle, -.mx_QuestionDialogTitle { +.mx_Dialog_title { min-height: 16px; padding: 12px; - border-bottom: 1px solid #a9dbf4; + border-bottom: 1px solid #a4a4a4; font-weight: bold; - font-size: 20px; + font-size: 18px; line-height: 1.4; } + +.mx_TextInputDialog_label { + text-align: left; + padding-bottom: 12px; +} + +.mx_TextInputDialog_input { + font-size: 15px; + border-radius: 3px; + border: 1px solid #f0f0f0; + padding: 9px; + color: #454545; +} diff --git a/src/skins/vector/css/hide.css b/src/skins/vector/css/hide.css index fbc2db20..f84a35b3 100644 --- a/src/skins/vector/css/hide.css +++ b/src/skins/vector/css/hide.css @@ -1,7 +1,3 @@ -.mx_RoomDropTarget, -.mx_RoomList_favourites_label, -.mx_RoomList_archive_label, -.mx_RoomHeader_search, .mx_RoomSettings_encrypt, .mx_CreateRoom_encrypt, .mx_RightPanel_filebutton diff --git a/src/skins/vector/css/organisms/CreateRoom.css b/src/skins/vector/css/matrix-react-sdk/structures/CreateRoom.css similarity index 91% rename from src/skins/vector/css/organisms/CreateRoom.css rename to src/skins/vector/css/matrix-react-sdk/structures/CreateRoom.css index d6b1765c..d9b121fe 100644 --- a/src/skins/vector/css/organisms/CreateRoom.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/CreateRoom.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ limitations under the License. */ .mx_CreateRoom { - width: 720px; + width: 960px; margin-left: auto; margin-right: auto; color: #4a4a4a; @@ -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/pages/MatrixChat.css b/src/skins/vector/css/matrix-react-sdk/structures/MatrixChat.css similarity index 70% rename from src/skins/vector/css/pages/MatrixChat.css rename to src/skins/vector/css/matrix-react-sdk/structures/MatrixChat.css index 6fa0415a..e8a5eb79 100644 --- a/src/skins/vector/css/pages/MatrixChat.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/MatrixChat.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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; @@ -35,7 +47,17 @@ limitations under the License. -webkit-order: 1; order: 1; - height: 21px; + height: 40px; +} + +.mx_GuestWarningBar { + -webkit-box-ordinal-group: 1; + -moz-box-ordinal-group: 1; + -ms-flex-order: 1; + -webkit-order: 1; + order: 1; + + height: 40px; } .mx_MatrixChat_toolbarShowing { @@ -69,8 +91,10 @@ limitations under the License. -webkit-order: 1; order: 1; - -webkit-flex: 0 0 230px; - flex: 0 0 230px; + background-color: #eaf5f0; + + -webkit-flex: 0 0 210px; + flex: 0 0 210px; } .mx_MatrixChat .mx_LeftPanel.collapsed { @@ -85,17 +109,25 @@ limitations under the License. -webkit-order: 2; order: 2; - padding-left: 12px; - padding-right: 12px; - background-color: #f3f8fa; + padding-left: 25px; + padding-right: 22px; + background-color: #fff; -webkit-flex: 1; flex: 1; + /* Experimental fix for https://github.com/vector-im/vector-web/issues/947 + and https://github.com/vector-im/vector-web/issues/946. + Empirically this stops the MessagePanel's width exploding outwards when + gemini is in 'prevented' mode + */ + overflow-x: auto; + /* XXX: Hack: apparently if you try to nest a flex-box * within a non-flex-box within a flex-box, the height * of the innermost element gets miscalculated if the - * parents are both auto. + * parents are both auto. Height has to be auto here + * for RoomView to correctly fit when the Toolbar is shown. * Ideally we'd launch straight into the RoomView at this * point, but instead we fudge it and make the middlePanel * flex itself. @@ -114,9 +146,8 @@ limitations under the License. -webkit-order: 3; order: 3; - background-color: #f3f8fa; - -webkit-flex: 0 0 230px; - flex: 0 0 230px; + -webkit-flex: 0 0 235px; + flex: 0 0 235px; } .mx_MatrixChat .mx_RightPanel.collapsed { diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomStatusBar.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomStatusBar.css new file mode 100644 index 00000000..d36035e6 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomStatusBar.css @@ -0,0 +1,92 @@ +.mx_RoomStatusBar { + margin-top: 5px; + margin-left: 65px; + min-height: 24px; +} + +/* position the indicator in the same place horizontally as .mx_EventTile_avatar. */ +.mx_RoomStatusBar_indicator { + padding-left: 18px; + padding-right: 12px; + margin-left: -73px; + float: left; + width: 24px; + text-align: center; +} + +.mx_RoomStatusBar_placeholderIndicator { + color: #4a4a4a; + opacity: 0.5; +} + +.mx_RoomStatusBar_scrollDownIndicator { + cursor: pointer; +} + +.mx_RoomStatusBar_unreadMessagesBar { + color: #ff0064; + cursor: pointer; +} + +.mx_RoomStatusBar_connectionLostBar { + margin-top: 19px; + height: 58px; +} + +.mx_RoomStatusBar_connectionLostBar img { + padding-left: 10px; + padding-right: 22px; + vertical-align: middle; + float: left; +} + +.mx_RoomStatusBar_connectionLostBar_title { + color: #ff0064; +} + +.mx_RoomStatusBar_connectionLostBar_desc { + color: #454545; + font-size: 13px; + opacity: 0.5; +} + +.mx_RoomStatusBar_resend_link { + color: #454545 ! important; + text-decoration: underline ! important; + cursor: pointer; +} + +.mx_RoomStatusBar_tabCompleteBar { + color: #4a4a4a; +} + +.mx_RoomStatusBar_typingBar { + color: #4a4a4a; + opacity: 0.5; + overflow-y: hidden; + display: block; +} + +.mx_RoomStatusBar_tabCompleteWrapper { + display: flex; + display: -webkit-flex; + height: 24px; +} + +.mx_RoomStatusBar_tabCompleteWrapper .mx_TabCompleteBar { + flex: 1 1 auto; + -webkit-flex: 1 1 auto; +} + +.mx_RoomStatusBar_tabCompleteEol { + flex: 0 0 auto; + -webkit-flex: 0 0 auto; + color: #76CFA6; +} + +.mx_RoomStatusBar_tabCompleteEol object { + vertical-align: middle; + margin-right: 8px; + margin-top: -2px; +} + diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css similarity index 66% rename from src/skins/vector/css/organisms/RoomView.css rename to src/skins/vector/css/matrix-react-sdk/structures/RoomView.css index 2aab203a..74fa198d 100644 --- a/src/skins/vector/css/organisms/RoomView.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,15 +36,15 @@ limitations under the License. -webkit-order: 1; order: 1; - -webkit-flex: 0 0 88px; - flex: 0 0 88px; + -webkit-flex: 0 0 83px; + flex: 0 0 83px; } .mx_RoomView_fileDropTarget { min-width: 0px; - max-width: 720px; + max-width: 960px; width: 100%; - font-size: 20px; + font-size: 18px; text-align: center; pointer-events: none; @@ -61,10 +61,10 @@ limitations under the License. border-top-right-radius: 10px; background-color: rgba(255, 255, 255, 0.9); - border: 2px dashed #80cef4; + border: 2px #e1dddd solid; border-bottom: none; position: absolute; - top: 88px; + top: 83px; bottom: 0px; z-index: 3000; } @@ -84,23 +84,31 @@ limitations under the License. order: 2; min-width: 0px; - max-width: 720px; + max-width: 960px; width: 100%; margin: auto; overflow: auto; - border-bottom: 1px solid #a8dbf3; + border-bottom: 1px solid #eee; -webkit-flex: 0 0 auto; flex: 0 0 auto; } -.mx_RoomView_messagePanel { +.mx_RoomView_topUnreadMessagesBar { -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; -webkit-order: 3; order: 3; +} + +.mx_RoomView_messagePanel { + -webkit-box-ordinal-group: 4; + -moz-box-ordinal-group: 4; + -ms-flex-order: 4; + -webkit-order: 4; + order: 4; -webkit-flex: 1 1 0; flex: 1 1 0; @@ -111,8 +119,22 @@ limitations under the License. } .mx_RoomView_messageListWrapper { - max-width: 720px; + max-width: 960px; margin: auto; + + min-height: 100%; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + + flex-direction: column; + -webkit-flex-direction: column; + + justify-content: flex-end; + -webkit-justify-content: flex-end; } .mx_RoomView_MessageList { @@ -129,8 +151,9 @@ limitations under the License. clear: both; margin-top: 32px; margin-bottom: 8px; + margin-left: 63px; padding-bottom: 6px; - border-bottom: 1px solid #a8dbf3; + border-bottom: 1px solid #eee; } .mx_RoomView_invitePrompt { @@ -141,7 +164,7 @@ limitations under the License. order: 2; min-width: 0px; - max-width: 720px; + max-width: 960px; width: 100%; margin: auto; @@ -149,55 +172,22 @@ limitations under the License. margin-bottom: 12px; } +li.mx_RoomView_myReadMarker_container { + height: 0px; + margin: 0px; + padding: 0px; + border: 0px; +} + +hr.mx_RoomView_myReadMarker { + border-top: solid 1px #76cfa6; + border-bottom: solid 1px #76cfa6; + margin-top: 0px; + position: relative; + top: 5px; +} + .mx_RoomView_statusArea { - -webkit-box-ordinal-group: 4; - -moz-box-ordinal-group: 4; - -ms-flex-order: 4; - -webkit-order: 4; - order: 4; - - width: 100%; - -webkit-flex: 0 0 58px; - flex: 0 0 58px; -} - -.mx_RoomView_statusAreaBox { - max-width: 720px; - margin: auto; - border-top: 1px solid #a8dbf3; -} - -.mx_RoomView_unreadMessagesBar { - margin-top: 13px; - color: #fff; - font-weight: bold; - background-color: #ff0064; - border-radius: 30px; - height: 30px; - line-height: 30px; - cursor: pointer; -} - -.mx_RoomView_unreadMessagesBar img { - padding-left: 22px; - padding-right: 22px; -} - -.mx_RoomView_typingBar { - margin-top: 17px; - margin-left: 56px; - color: #818794; -} - -.mx_RoomView_typingBar img { - padding-left: 12px; - padding-right: 12px; - margin-left: -64px; - margin-top: -7px; - float: left; -} - -.mx_RoomView .mx_MessageComposer { -webkit-box-ordinal-group: 5; -moz-box-ordinal-group: 5; -ms-flex-order: 5; @@ -205,44 +195,62 @@ limitations under the License. order: 5; width: 100%; - -webkit-flex: 0 0 63px; - flex: 0 0 63px; - margin-right: 2px; + -webkit-flex: 0 0 auto; + flex: 0 0 auto; } -.mx_RoomView_uploadProgressOuter { - width: 100%; - background-color: rgba(169, 219, 244, 0.5); - height: 4px; +.mx_RoomView_statusAreaBox { + max-width: 960px; + margin: auto; + min-height: 36px; } -.mx_RoomView_uploadProgressInner { - background-color: #80cef4; - height: 4px; +.mx_RoomView_statusAreaBox_line { + border-top: 1px solid #eee; + height: 1px; } -.mx_RoomView_uploadFilename { - margin-top: 15px; - margin-left: 56px; +.mx_RoomView_inCall .mx_RoomView_statusAreaBox_line { + border-top: 1px hidden; } -.mx_RoomView_uploadIcon { - float: left; - margin-top: 6px; - margin-left: 5px; +.mx_RoomView_inCall .mx_MessageComposer_wrapper { + border-top: 2px hidden; } -.mx_RoomView_uploadCancel { +.mx_RoomView_inCall .mx_RoomView_statusAreaBox { + background-color: #76CFA6; + color: #fff; + position: relative; +} + +.mx_RoomView_voipChevron { + position: absolute; + bottom: -11px; + right: 11px; +} + +.mx_RoomView_voipButton { float: right; - margin-top: 6px; - margin-right: 10px; + margin-right: 13px; + cursor: pointer; } -.mx_RoomView_uploadBytes { - float: right; - opacity: 0.5; - margin-top: 15px; - margin-right: 10px; +.mx_RoomView_voipButton object { + pointer-events: none; +} + +.mx_RoomView .mx_MessageComposer { + -webkit-box-ordinal-group: 6; + -moz-box-ordinal-group: 6; + -ms-flex-order: 6; + -webkit-order: 6; + order: 6; + + width: 100%; + -webkit-flex: 0 0 auto; + flex: 0 0 auto; + margin-right: 2px; } .mx_RoomView_ongoingConfCallNotification { @@ -251,5 +259,5 @@ limitations under the License. background-color: #ff0064; color: #fff; font-weight: bold; - padding: 6px; -} \ No newline at end of file + padding: 6px 0; +} diff --git a/src/skins/vector/css/matrix-react-sdk/structures/UploadBar.css b/src/skins/vector/css/matrix-react-sdk/structures/UploadBar.css new file mode 100644 index 00000000..bff271a3 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/structures/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/matrix-react-sdk/structures/UserSettings.css b/src/skins/vector/css/matrix-react-sdk/structures/UserSettings.css new file mode 100644 index 00000000..ea1710da --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/structures/UserSettings.css @@ -0,0 +1,184 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_UserSettings { + max-width: 960px; + width: 100%; + margin-left: auto; + margin-right: auto; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + flex-direction: column; + -webkit-flex-direction: column; +} + +.mx_UserSettings .mx_RoomHeader { + -webkit-box-ordinal-group: 1; + -moz-box-ordinal-group: 1; + -ms-flex-order: 1; + -webkit-order: 1; + order: 1; + + -webkit-flex: 0 0 83px; + flex: 0 0 83px; +} + +.mx_UserSettings_body { + -webkit-box-ordinal-group: 2; + -moz-box-ordinal-group: 2; + -ms-flex-order: 2; + -webkit-order: 2; + order: 2; + + -webkit-flex: 1 1 0; + flex: 1 1 0; + + overflow-y: auto; +} + +.mx_UserSettings_spinner { + display: inline-block; + vertical-align: middle; + margin-right: 12px; + width: 32px; + height: 32px; +} + +.mx_UserSettings_button { + display: inline; + vertical-align: middle; + border: 0px; + border-radius: 36px; + font-weight: 400; + font-size: 16px; + color: #fff; + background-color: #76cfa6; + width: auto; + margin: auto; + padding: 7px; + padding-left: 1.5em; + padding-right: 1.5em; + cursor: pointer; +} + +.mx_UserSettings h2 { + clear: both; + margin-top: 32px; + margin-bottom: 8px; + margin-left: 63px; + padding-bottom: 6px; + border-bottom: 1px solid #eee; +} + +.mx_UserSettings h3 { + font-weight: bold; + font-size: 15px; + margin-top: 4px; + margin-bottom: 4px; +} + +.mx_UserSettings_section { + margin-left: 63px; + margin-top: 28px; + margin-bottom: 28px; +} + +.mx_UserSettings_accountTable +.mx_UserSettings_notifTable +{ + display: table; +} + +.mx_UserSettings_profileTable +{ + display: table; + float: left; +} + +.mx_UserSettings_profileTableRow +{ + display: table-row; +} + +.mx_UserSettings_profileLabelCell +{ + padding-bottom: 21px; + display: table-cell; + font-weight: bold; + padding-right: 24px; +} + +.mx_UserSettings_profileInputCell { + display: table-cell; + padding-bottom: 21px; + width: 240px; +} + +.mx_UserSettings_profileInputCell input, +.mx_UserSettings_profileInputCell .mx_EditableText +{ + display: inline-block; + border: 0px; + border-bottom: 1px solid rgba(151, 151, 151, 0.5); + padding: 0px; + width: 240px; + color: rgba(74, 74, 74, 0.9); + font-family: 'Open Sans', Helvetica, Arial, Sans-Serif; + font-size: 16px; +} + +.mx_UserSettings_addThreepid { + display: table-cell; + padding-left: 0.5em; + position: relative; + cursor: pointer; +} + +.mx_UserSettings_changePasswordButton { + float: right; + margin-right: 32px; + margin-left: 32px; +} + +.mx_UserSettings_logout { + float: right; + margin-right: 32px; + margin-left: 32px; +} + +.mx_UserSettings_avatarPicker { + margin-left: 32px; + margin-right: 32px; + float: right; + cursor: pointer; +} + +.mx_UserSettings_avatarPicker_img .mx_BaseAvatar_image { + object-fit: cover; +} + +.mx_UserSettings_avatarPicker_edit { + text-align: center; + margin-top: 10px; +} + +.mx_UserSettings_avatarPicker_edit > input { + display: none; +} diff --git a/src/skins/vector/css/templates/Login.css b/src/skins/vector/css/matrix-react-sdk/structures/login/Login.css similarity index 75% rename from src/skins/vector/css/templates/Login.css rename to src/skins/vector/css/matrix-react-sdk/structures/login/Login.css index 93cb7433..5ee58004 100644 --- a/src/skins/vector/css/templates/Login.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/login/Login.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,18 @@ limitations under the License. .mx_Login { width: 100%; height: 100%; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + align-items: center; + -webkit-justify-content: center; + justify-content: center; + + overflow: auto; } .mx_Login h2 { @@ -28,8 +40,10 @@ limitations under the License. .mx_Login_box { width: 300px; + min-height: 450px; + padding-top: 50px; + padding-bottom: 50px; margin: auto; - padding-top: 100px; } .mx_Login_logo { @@ -41,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; } @@ -54,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; } @@ -71,7 +85,7 @@ limitations under the License. display: block; text-align: center; width: 100%; - font-size: 14px; + font-size: 13px; opacity: 0.8; } @@ -83,7 +97,7 @@ limitations under the License. display: block; text-align: center; width: 100%; - font-size: 14px; + font-size: 13px; opacity: 0.8; } @@ -91,6 +105,15 @@ limitations under the License. color: #4a4a4a; } +.mx_Login_forgot { + font-size: 13px; + opacity: 0.8; +} + +.mx_Login_forgot:link { + color: #4a4a4a; +} + .mx_Login_loader { position: absolute; left: 50%; diff --git a/src/skins/vector/css/molecules/MatrixToolbar.css b/src/skins/vector/css/matrix-react-sdk/views/avatars/BaseAvatar.css similarity index 67% rename from src/skins/vector/css/molecules/MatrixToolbar.css rename to src/skins/vector/css/matrix-react-sdk/views/avatars/BaseAvatar.css index 99c28240..901a2959 100644 --- a/src/skins/vector/css/molecules/MatrixToolbar.css +++ b/src/skins/vector/css/matrix-react-sdk/views/avatars/BaseAvatar.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,21 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MatrixToolbar { - text-align: center; - background-color: #ff0064; +.mx_BaseAvatar { + position: relative; +} + +.mx_BaseAvatar_initial { + position: absolute; + z-index: 1; color: #fff; - font-weight: bold; - padding: 6px; + text-align: center; + speak: none; + pointer-events: none; + font-weight: normal; } -.mx_MatrixToolbar button { - margin-left: 12px; +.mx_BaseAvatar_image { + border-radius: 40px; + vertical-align: top; } - -.mx_MatrixToolbar_close { - float: right; - margin-top: 3px; - margin-right: 12px; - cursor: pointer; -} \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/SetDisplayNameDialog.css b/src/skins/vector/css/matrix-react-sdk/views/dialogs/SetDisplayNameDialog.css new file mode 100644 index 00000000..66a79672 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/SetDisplayNameDialog.css @@ -0,0 +1,23 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SetDisplayNameDialog_input { + border-radius: 3px; + border: 1px solid #f0f0f0; + padding: 9px; + color: #454545; + font-size: 15px; +} \ No newline at end of file diff --git a/src/skins/vector/css/molecules/ProgressBar.css b/src/skins/vector/css/matrix-react-sdk/views/elements/ProgressBar.css similarity index 94% rename from src/skins/vector/css/molecules/ProgressBar.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/ProgressBar.css index 8b8adc09..7b5e0c74 100644 --- a/src/skins/vector/css/molecules/ProgressBar.css +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/ProgressBar.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/skins/vector/css/molecules/ServerConfig.css b/src/skins/vector/css/matrix-react-sdk/views/login/ServerConfig.css similarity index 92% rename from src/skins/vector/css/molecules/ServerConfig.css rename to src/skins/vector/css/matrix-react-sdk/views/login/ServerConfig.css index db0572c8..75cd4170 100644 --- a/src/skins/vector/css/molecules/ServerConfig.css +++ b/src/skins/vector/css/matrix-react-sdk/views/login/ServerConfig.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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/molecules/MImageTile.css b/src/skins/vector/css/matrix-react-sdk/views/messages/MImageBody.css similarity index 66% rename from src/skins/vector/css/molecules/MImageTile.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/MImageBody.css index 10e5b50b..8af3701e 100644 --- a/src/skins/vector/css/molecules/MImageTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/MImageBody.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MImageTile_thumbnail { +.mx_MImageBody_thumbnail { + max-width: 100%; /* background-color: #fff; border: 2px solid #fff; @@ -22,16 +23,20 @@ limitations under the License. */ } -.mx_MImageTile_download { - color: #80cef4; +.mx_MImageBody_download { + color: #76cfa6; cursor: pointer; } -.mx_MImageTile_download a { - color: #80cef4; +.mx_MImageBody_download a { + color: #76cfa6; text-decoration: none; } -.mx_MImageTile_download img { - padding-right: 8px; -} \ No newline at end of file +.mx_MImageBody_download object { + margin-left: -16px; + padding-right: 4px; + margin-top: -4px; + vertical-align: middle; + pointer-events: none; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css b/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css new file mode 100644 index 00000000..a88c2086 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css @@ -0,0 +1,20 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MNoticeBody { + white-space: pre-wrap; + opacity: 0.6; +} diff --git a/src/skins/vector/css/molecules/MTextTile.css b/src/skins/vector/css/matrix-react-sdk/views/messages/MTextBody.css similarity index 91% rename from src/skins/vector/css/molecules/MTextTile.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/MTextBody.css index 5b117e41..93a89ad1 100644 --- a/src/skins/vector/css/molecules/MTextTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/MTextBody.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MTextTile { +.mx_MTextBody { white-space: pre-wrap; } - diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css b/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css new file mode 100644 index 00000000..be7565b3 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css @@ -0,0 +1,20 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_TextualEvent { + opacity: 0.5; + overflow-y: hidden; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/EntityTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/EntityTile.css new file mode 100644 index 00000000..be7b474d --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/EntityTile.css @@ -0,0 +1,108 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_EntityTile { + display: table-row; + position: relative; + color: #454545; + cursor: pointer; +} + +.mx_EntityTile_invite { + display: table-cell; + vertical-align: middle; + margin-left: 10px; + width: 26px; +} + +.mx_EntityTile_avatar { + display: table-cell; + padding-left: 3px; + padding-right: 12px; + padding-top: 4px; + padding-bottom: 4px; + vertical-align: middle; + width: 36px; + height: 36px; + position: relative; +} + +.mx_EntityTile_power { + position: absolute; + width: 16px; + height: 17px; + top: 0px; + right: 6px; +} + +.mx_EntityTile_name { + display: table-cell; + vertical-align: middle; + overflow: hidden; + text-overflow: ellipsis; +} + +.mx_EntityTile_details { + display: table-cell; + padding-right: 14px; + vertical-align: middle; +} + +.mx_EntityTile_name_hover { + font-size: 13px; + overflow: hidden; + text-overflow: ellipsis; +} + +.mx_EntityTile_chevron { + margin-top: 8px; + margin-right: -4px; + margin-left: 6px; + float: right; +} + +.mx_EntityTile_ellipsis .mx_EntityTile_name { + font-style: italic; + font-color: #454545; +} + +.mx_EntityTile_invitePlaceholder .mx_EntityTile_name { + font-style: italic; + font-color: #454545; +} + +.mx_EntityTile_unavailable .mx_EntityTile_avatar, +.mx_EntityTile_unavailable .mx_EntityTile_name, +.mx_EntityTile_unavailable .mx_EntityTile_name_hover +{ + opacity: 0.66; +} + +.mx_EntityTile_offline .mx_EntityTile_avatar, +.mx_EntityTile_offline .mx_EntityTile_name, +.mx_EntityTile_offline .mx_EntityTile_name_hover +{ + opacity: 0.25; +} + +.mx_EntityTile_unknown .mx_EntityTile_avatar, +.mx_EntityTile_unknown .mx_EntityTile_name, +.mx_EntityTile_unknown .mx_EntityTile_name_hover +{ + opacity: 0.25; +} + + diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css new file mode 100644 index 00000000..c4ed9acb --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css @@ -0,0 +1,192 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_EventTile { + max-width: 100%; + clear: both; + margin-top: 24px; + margin-left: 65px; +} + +.mx_EventTile_avatar { + padding-left: 18px; + padding-right: 12px; + margin-left: -73px; + margin-top: -2px; + float: left; + position: relative; + top: 0px; +} + +.mx_EventTile_continuation { + margin-top: 8px ! important; +} + +.mx_EventTile .mx_SenderProfile { + color: #454545; + opacity: 0.5; + font-size: 13px; + margin-bottom: 4px; + display: block; + overflow-y: hidden; +} + +.mx_EventTile .mx_MessageTimestamp { + color: #acacac; + font-size: 11px; +} + +.mx_EventTile_line { + position: relative; + /* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */ + margin-right: 95px; +} + +.mx_EventTile_content { + display: block; + overflow-y: hidden; +} + +/* Various markdown overrides */ + +.mx_EventTile_content .markdown-body { + font-family: inherit ! important; + white-space: normal ! important; + line-height: inherit ! important; + color: inherit; + font-size: 15px; +} + +.mx_EventTile_content .markdown-body h1, +.mx_EventTile_content .markdown-body h2, +.mx_EventTile_content .markdown-body h3, +.mx_EventTile_content .markdown-body h4, +.mx_EventTile_content .markdown-body h5, +.mx_EventTile_content .markdown-body h6 +{ + font-family: inherit ! important; +} + +.mx_EventTile_content .markdown-body a { + color: #76cfa6; +} + +.mx_EventTile_content .markdown-body .hljs { + display: inline ! important; +} + +/* end of overrides */ + +/* this is used for the tile for the event which is selected via the URL. + * TODO: ultimately we probably want some transition on here. + */ +.mx_EventTile_selected { + border-left: #76cfa6 5px solid; + margin-left: 53px; + padding-left: 7px; +} + +.mx_EventTile_searchHighlight { + background-color: #76cfa6; + color: #fff; + border-radius: 5px; + padding-left: 2px; + padding-right: 2px; + cursor: pointer; +} + +.mx_EventTile_searchHighlight a { + background-color: #76cfa6; + color: #fff; +} + +.mx_EventTile_sending { + color: #ddd; +} + +.mx_EventTile_notSent { + color: #f44; +} + +.mx_EventTile_highlight, +.mx_EventTile_highlight .markdown-body + { + color: #FF0064; +} + +.mx_EventTile_contextual { + opacity: 0.4; +} + +.mx_EventTile_msgOption { + float: right; + text-align: right; + z-index: 1; + position: relative; + width: 90px; + height: 1px; /* Hack to stop the height of this pushing the messages apart. Replaces marigin-top: -6px. This interacts better with a read marker being in between. Content overflows. */ + margin-right: 10px; +} + +.mx_EventTile .mx_MessageTimestamp { + display: block; + visibility: hidden; + text-align: right; +} + +.mx_EventTile_last .mx_MessageTimestamp { + visibility: visible; +} + +.mx_EventTile:hover .mx_MessageTimestamp { + visibility: visible; +} + +.mx_EventTile_editButton { + position: absolute; + display: inline-block; + visibility: hidden; +} + +.mx_EventTile:hover .mx_EventTile_editButton { + visibility: visible; +} + +.mx_EventTile.menu .mx_EventTile_editButton { + visibility: visible; +} + +.mx_EventTile.menu .mx_MessageTimestamp { + visibility: visible; +} + +.mx_EventTile_readAvatars { + position: relative; + display: inline-block; + width: 14px; + height: 14px; +} + +.mx_EventTile_readAvatars .mx_BaseAvatar { + position: absolute; + display: inline-block; +} + +.mx_EventTile_readAvatarRemainder { + color: #acacac; + font-size: 11px; + position: absolute; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberInfo.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberInfo.css new file mode 100644 index 00000000..4d421007 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberInfo.css @@ -0,0 +1,65 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MemberInfo { + height: 100%; +} + +.mx_MemberInfo h2 { + margin-top: 6px; +} + +.mx_MemberInfo_cancel { + float: right; + margin-right: 18px; + cursor: pointer; +} + +.mx_MemberInfo_avatar { + clear: both; +} + +.mx_MemberInfo_profile { + margin-bottom: 16px; +} + +.mx_MemberInfo h3 { + text-transform: uppercase; + color: #3d3b39; + font-weight: 600; + font-size: 13px; + margin-top: 16px; + margin-bottom: 14px; +} + +.mx_MemberInfo_profileField { + font-color: #999999; + font-size: 13px; + position: relative; + background-color: #fff; +} + +.mx_MemberInfo_buttons { + margin-bottom: 16px; +} + +.mx_MemberInfo_field { + cursor: pointer; + font-size: 13px; + color: #76cfa6; + margin-left: 8px; + line-height: 23px; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberList.css new file mode 100644 index 00000000..fb24cc3c --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberList.css @@ -0,0 +1,106 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MemberList { + height: 100%; + + -webkit-flex: 1; + flex: 1; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + + flex-direction: column; + -webkit-flex-direction: column; +} + +.mx_MemberList_chevron { + position: absolute; + right: 35px; + margin-top: -15px; +} + +.mx_MemberList_border { + overflow-y: auto; + + order: 1; + -webkit-flex: 1 1 0; + flex: 1 1 0px; +} + +.mx_MemberList .mx_SearchableEntityList { + order: 1; + flex: 0 0 auto; + -webkit-flex: 0 0 auto; +} + +.mx_MemberList .mx_SearchableEntityList_expanded { + flex: 1 0 0; + -webkit-flex: 1 0 0; +} + +.mx_MemberList_joined { + order: 2; + flex: 1 0 0; + -webkit-flex: 1 0 0; + + overflow-y: auto; +} + +/* +.mx_MemberList_invited { + order: 3; + flex: 0 0 100px; + -webkit-flex: 0 0 100px; + overflow-y: auto; +} +*/ + +.mx_MemberList_bottom { + order: 4; + flex: 0 0 72px; + -webkit-flex: 0 0 72px; +} + +.mx_MemberList_bottomRule { + border-top: 2px solid #e1dddd; + margin-right: 15px; +} + +.mx_MemberList_invited h2 { + text-transform: uppercase; + color: #3d3b39; + font-weight: 600; + font-size: 13px; + padding-left: 3px; + padding-right: 12px; + margin-top: 8px; + margin-bottom: 4px; +} + +/* we have to have display: table in order for the horizontal wrapping to work */ +.mx_MemberList_wrapper { + display: table; + table-layout: fixed; + width: 100%; +} + +.mx_MemberList_outerWrapper { + height: 0px; +} diff --git a/src/skins/vector/css/molecules/MessageComposer.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/MessageComposer.css similarity index 56% rename from src/skins/vector/css/molecules/MessageComposer.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/MessageComposer.css index af4934ee..6eca5415 100644 --- a/src/skins/vector/css/molecules/MessageComposer.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/MessageComposer.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,70 +15,90 @@ limitations under the License. */ .mx_MessageComposer_wrapper { - max-width: 720px; - height: 50px; + max-width: 960px; vertical-align: middle; margin: auto; - background-color: #fff; - border-radius: 25px; - border: 1px solid #a9dbf4; + border-top: 2px solid #e1dddd; } .mx_MessageComposer_row { display: table-row; width: 100%; - height: 50px; } .mx_MessageComposer .mx_MessageComposer_avatar { display: table-cell; - padding-left: 5px; - padding-right: 10px; - height: 50px; + padding-left: 10px; + padding-right: 28px; + vertical-align: middle; } -.mx_MessageComposer .mx_MessageComposer_avatar img { - margin-top: 5px; - border-radius: 20px; - background-color: #dbdbdb; +.mx_MessageComposer .mx_MessageComposer_avatar .mx_BaseAvatar { + display: block; } .mx_MessageComposer_input { display: table-cell; width: 100%; vertical-align: middle; - height: 50px; + height: 70px; } .mx_MessageComposer_input textarea { + display: block; font-size: 15px; width: 100%; - height: 1.2em; - padding-top: 0.7em; - padding-bottom: 0.7em; + padding: 0px; + margin-top: 6px; + margin-bottom: 6px; border: 0px; resize: none; outline: none; -webkit-box-shadow: none; -moz-box-shadow: none; - box-shadow: none; + box-shadow: none; /* needed for FF */ - font-family: 'Lato', Helvetica, Arial, Sans-Serif; + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; } /* hack for FF as vertical alignment of custom placeholder text is broken */ .mx_MessageComposer_input textarea::-moz-placeholder { line-height: 100%; + color: #76cfa6; +} +.mx_MessageComposer_input textarea::-webkit-input-placeholder { + color: #76cfa6; } -.mx_MessageComposer_upload { +.mx_MessageComposer_upload, +.mx_MessageComposer_hangup, +.mx_MessageComposer_voicecall, +.mx_MessageComposer_videocall { display: table-cell; vertical-align: middle; - padding-right: 15px; + padding-left: 10px; + padding-right: 10px; cursor: pointer; } -.mx_MessageComposer_upload img { +.mx_MessageComposer_upload object, +.mx_MessageComposer_hangup object, +.mx_MessageComposer_voicecall object, +.mx_MessageComposer_videocall object { + pointer-events: none; +} + +.mx_MessageComposer_videocall { + padding-right: 10px; + padding-top: 4px; +} + +.mx_MessageComposer_voicecall { + padding-right: 10px; + padding-top: 4px; +} + +.mx_MessageComposer_upload object { margin-top: 5px; } diff --git a/src/skins/vector/css/molecules/MNoticeTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/PresenceLabel.css similarity index 88% rename from src/skins/vector/css/molecules/MNoticeTile.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/PresenceLabel.css index 0e9c3ca1..682c849c 100644 --- a/src/skins/vector/css/molecules/MNoticeTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/PresenceLabel.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MNoticeTile { +.mx_PresenceLabel { + font-size: 11px; opacity: 0.5; -} +} \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomHeader.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomHeader.css new file mode 100644 index 00000000..664c3170 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomHeader.css @@ -0,0 +1,258 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* add 20px to the height of the header when editing */ +.mx_RoomHeader_editing { + -webit-flex: 0 0 93px ! important; + flex: 0 0 93px ! important; +} + +.mx_RoomHeader_wrapper { + max-width: 960px; + margin: auto; + height: 83px; + border-bottom: 1px solid #eeeeee; + + -webkit-align-items: center; + align-items: center; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; +} + +.mx_RoomHeader_editing .mx_RoomHeader_wrapper { + border-bottom: 1px solid transparent; +} + +.mx_RoomHeader_leftRow { + margin-left: -2px; + + -webkit-box-ordinal-group: 1; + -moz-box-ordinal-group: 1; + -ms-flex-order: 1; + -webkit-order: 1; + order: 1; + + -webkit-flex: 1; + flex: 1; +} + +.mx_RoomHeader_textButton { + height: 36px; + background-color: #76cfa6; + border-radius: 36px; + margin-right: 8px; + color: #fff; + line-height: 34px; + margin-top: -2px; + text-align: center; + + -webkit-box-ordinal-group: 2; + -moz-box-ordinal-group: 2; + -ms-flex-order: 2; + -webkit-order: 2; + order: 2; + + cursor: pointer; + +/* + -webkit-flex: 0 0 90px; + flex: 0 0 90px; +*/ + padding-left: 12px; + padding-right: 12px; +} + +.mx_RoomHeader_cancelButton { + -webkit-box-ordinal-group: 2; + -moz-box-ordinal-group: 2; + -ms-flex-order: 2; + -webkit-order: 2; + order: 2; + + cursor: pointer; + + padding-left: 12px; + padding-right: 12px; +} + +.mx_RoomHeader_rightRow { + margin-top: 4px; + background-color: #fff; + + -webkit-box-ordinal-group: 3; + -moz-box-ordinal-group: 3; + -ms-flex-order: 3; + -webkit-order: 3; + order: 3; +} + +.mx_RoomHeader_info { + display: table-cell; + width: 100%; + vertical-align: middle; +} + +.mx_RoomHeader_simpleHeader { + line-height: 83px; + color: #454545; + font-size: 22px; + font-weight: bold; + overflow: hidden; + margin-left: 63px; + text-overflow: ellipsis; + width: 100%; +} + +.mx_RoomHeader_simpleHeaderCancel { + float: right; + margin-top: 8px; + padding: 24px; + cursor: pointer; +} + +.mx_RoomHeader_name { + vertical-align: middle; + width: 100%; + height: 31px; + overflow: hidden; + color: #454545; + font-weight: bold; + font-size: 22px; + padding-left: 19px; + padding-right: 16px; + /* why isn't text-overflow working? */ + text-overflow: ellipsis; + border-bottom: 1px solid transparent; +} + +.mx_RoomHeader_nametext { + display: inline-block; +} + +.mx_RoomHeader_settingsHint { + color: #a2a2a2 ! important; +} + +.mx_RoomHeader_searchStatus { + display: inline-block; + font-weight: normal; + opacity: 0.6; +} + +.mx_RoomHeader_settingsButton { + display: inline-block; + visibility: hidden; + position: relative; + bottom: 10px; + left: 4px; +} + +.mx_RoomHeader_settingsButton object { + pointer-events: none; +} + +.mx_RoomHeader_name, +.mx_RoomHeader_avatar, +.mx_RoomHeader_avatarPicker, +.mx_RoomHeader_avatarPicker_edit { + cursor: pointer; +} + +.mx_RoomHeader_name:hover div:not(.mx_RoomHeader_editable) { + color: #76cfa6; +} + +.mx_RoomHeader_name:hover .mx_RoomHeader_settingsButton { + visibility: visible; +} + +.mx_RoomHeader_leaveButton { + margin-top: -1px; +} + +.mx_RoomHeader_placeholder { + color: #a2a2a2 ! important; +} + +.mx_RoomHeader_editable { + border-bottom: 1px solid #c7c7c7 ! important; + min-width: 150px; + cursor: text; +} + +.mx_RoomHeader_editable:focus { + border-bottom: 1px solid #76CFA6 ! important; + outline: none; + box-shadow: none; +} + +.mx_RoomHeader_topic { + vertical-align: bottom; + float: left; + max-height: 42px; + color: #454545; + font-weight: 300; + margin-left: 19px; + margin-right: 16px; + overflow: hidden; + text-overflow: ellipsis; + border-bottom: 1px solid transparent; +} + +.mx_RoomHeader_avatar { + display: table-cell; + width: 48px; + height: 50px; + vertical-align: middle; +} + +.mx_RoomHeader_avatarPicker_edit { + position: absolute; + margin-left: 16px; + margin-top: 4px; +} + +.mx_RoomHeader_avatarPicker_edit > label { + cursor: pointer; +} + +.mx_RoomHeader_avatarPicker_edit > input { + display: none; +} + +.mx_RoomHeader_button { + display: table-cell; + vertical-align: top; + padding-left: 8px; + padding-right: 8px; + cursor: pointer; +} + +.mx_RoomHeader_button object { + pointer-events: none; +} + +.mx_RoomHeader_voipButton { + display: table-cell; +} + +.mx_RoomHeader_voipButtons { + margin-top: 18px; +} diff --git a/src/skins/vector/css/molecules/RoomDropTarget.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomList.css similarity index 53% rename from src/skins/vector/css/molecules/RoomDropTarget.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/RoomList.css index c42d4499..1a4ec869 100644 --- a/src/skins/vector/css/molecules/RoomDropTarget.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomList.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,14 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RoomDropTarget { - font-size: 14px; - text-align: center; - margin-left: 8px; - margin-right: 8px; - padding-top: 16px; - padding-bottom: 16px; - background-color: #fbfbfb; - border: 1px dashed #d7d7d7; - border-radius: 8px; +.mx_RoomList { + padding-top: 24px; + padding-bottom: 12px; + min-height: 400px; +} + +.mx_RoomList_expandButton { + margin-left: 8px; + cursor: pointer; + 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/MemberList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css similarity index 57% rename from src/skins/vector/css/organisms/MemberList.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css index aab0def4..5a6ee551 100644 --- a/src/skins/vector/css/organisms/MemberList.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,47 +14,43 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberList { - height: 100%; - margin-bottom: 100px; - padding: 8px; +.mx_RoomPreviewBar { + text-align: center; + height: 176px; - -webkit-flex: 1; - flex: 1; + -webkit-align-items: center; + align-items: center; + + flex-direction: column; + -webkit-flex-direction: column; + + justify-content: center; + -webkit-justify-content: center; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - - flex-direction: column; - -webkit-flex-direction: column; } -.mx_MemberList_chevron { - position: absolute; - right: 35px; - margin-top: -15px; +.mx_RoomPreviewBar_wrapper { } -.mx_MemberList_border { - border: 1px solid #a9dbf4; - overflow-y: auto; - border-radius: 8px; - background-color: #fff; - - order: 1; - -webkit-flex: 1 1 0; - flex: 1 1 0px; +.mx_RoomPreviewBar_invite_text { + color: #454545; } -.mx_MemberList_wrapper { - display: table; - table-layout: fixed; - width: 100%; +.mx_RoomPreviewBar_join_text { + color: #ff0064; } -.mx_MemberList h2 { - margin: 14px; +.mx_RoomPreviewBar_preview_text { + margin-top: 25px; + color: #a4a4a4; +} + +.mx_RoomPreviewBar_join_text a { + text-decoration: underline; + cursor: pointer; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomSettings.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomSettings.css new file mode 100644 index 00000000..172da8eb --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomSettings.css @@ -0,0 +1,192 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomSettings { + margin-left: 65px; + margin-bottom: 20px; +} + +.mx_RoomSettings_powerLevels { + display: table; +} + +.mx_RoomSettings_powerLevel { + display: table-row; +} + +.mx_RoomSettings_powerLevelKey, +.mx_RoomSettings_powerLevel .mx_PowerSelector { + display: table-cell; + padding-bottom: 5px; +} + +.mx_RoomSettings_powerLevelKey { + text-align: right; + padding-right: 0.3em; +} + +.mx_RoomSettings h3 { + text-transform: uppercase; + color: #3d3b39; + font-weight: 600; + font-size: 13px; + margin-top: 36px; + margin-bottom: 10px; +} + +/* +.mx_RoomSettings input, +.mx_RoomSettings textarea { + border-radius: 3px; + border: 1px solid #c7c7c7; + font-weight: 300; + font-size: 13px; + padding: 9px; + margin-top: 6px; +} +*/ + +.mx_RoomSettings .mx_RoomSettings_toggles label { + margin-bottom: 8px; + display: block; +} + +.mx_RoomSettings .mx_RoomSettings_toggles input[type="checkbox"], +.mx_RoomSettings .mx_RoomSettings_toggles input[type="radio"] { + margin-right: 7px; +} + +.mx_RoomSettings .mx_RoomSettings_tags input[type="checkbox"] { + margin-left: 1em; + margin-right: 7px; +} + +.mx_RoomSettings .mx_RoomSettings_tags { + margin-bottom: 8px; +} + +.mx_RoomSettings .mx_RoomSettings_roomColor { + display: inline-block; + position: relative; + width: 37px; + height: 37px; + border: 1px solid #979797; + margin-right: 13px; +} + +.mx_RoomSettings .mx_RoomSettings_roomColor_selected { + position: absolute; + left: 10px; + top: 4px; +} + +.mx_RoomSettings .mx_RoomSettings_roomColorPrimary { + height: 10px; + position: absolute; + bottom: 0px; + width: 100%; +} + +.mx_RoomSettings .mx_RoomSettings_aliasLabel { + margin-bottom: 8px; +} + +.mx_RoomSettings .mx_RoomSettings_aliasesTable { + margin-top: 12px; + margin-bottom: 0px; + margin-left: 56px; + display: table; +} + +.mx_RoomSettings .mx_RoomSettings_aliasesTableRow { + display: table-row; + margin-bottom: 16px; +} + +.mx_RoomSettings .mx_RoomSettings_alias { + max-width: 400px; + margin-bottom: 16px; + /* + commented out so margin applies + display: table-cell; */ +} + +.mx_RoomSettings .mx_RoomSettings_addAlias, +.mx_RoomSettings .mx_RoomSettings_deleteAlias { + display: table-cell; + padding-left: 0.5em; + position: relative; + cursor: pointer; +} + +.mx_RoomSettings .mx_RoomSettings_addAlias img, +.mx_RoomSettings .mx_RoomSettings_deleteAlias img { + visibility: hidden; +} + +.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_addAlias img, +.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_deleteAlias img { + visibility: visible; +} + +.mx_RoomSettings_editable { + border: 0px; + border-bottom: 1px solid #c7c7c7; + padding: 0px; + min-width: 240px; +} + +.mx_RoomSettings_editable:focus { + border-bottom: 1px solid #76CFA6; + outline: none; + box-shadow: none; +} + +.mx_RoomSettings_deleteAlias, +.mx_RoomSettings_addAlias { + display: table-cell; + visibility: visible; +} + +.mx_RoomSettings_deleteAlias:hover, +.mx_RoomSettings_addAlias:hover { + visibility: visible; +} + +.mx_RoomSettings_aliasPlaceholder { + color: #a2a2a2; +} + +.mx_RoomSettings_buttons { + text-align: right; + margin-bottom: 16px; +} + +.mx_RoomSettings_button { + display: inline; + border: 0px; + height: 36px; + border-radius: 36px; + font-weight: 400; + font-size: 15px; + color: #fff; + background-color: #76cfa6; + width: auto; + margin: auto; + padding: 6px; + padding-left: 1em; + padding-right: 1em; +} \ No newline at end of file diff --git a/src/skins/vector/css/molecules/RoomTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css similarity index 55% rename from src/skins/vector/css/molecules/RoomTile.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css index 511fc94f..f562c76c 100644 --- a/src/skins/vector/css/molecules/RoomTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,33 +16,41 @@ limitations under the License. .mx_RoomTile { cursor: pointer; - display: table-row; - color: #818794; + /* This fixes wrapping of long room names, but breaks drag & drop previews */ + /* display: table-row; */ + font-size: 13px; } .mx_RoomTile_avatar { display: table-cell; - padding-right: 10px; - padding-top: 3px; - padding-bottom: 3px; - padding-left: 10px; - vertical-align: middle; - width: 36px; - height: 36px; + padding-right: 8px; + padding-top: 6px; + padding-bottom: 6px; + padding-left: 18px; + width: 24px; + height: 24px; position: relative; -} - -.mx_RoomTile_avatar img { - border-radius: 20px; - background-color: #dbdbdb; + vertical-align: middle; } .mx_RoomTile_name { display: table-cell; + width: 100%; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; padding-right: 16px; + color: rgba(69, 69, 69, 0.8); +} + +.mx_RoomTile_ellipsis .mx_RoomTile_name { + font-style: italic; + color: #454545; +} + +.mx_RoomTile_invite { +/* color: rgba(69, 69, 69, 0.5); +*/ } .collapsed .mx_RoomTile_name { @@ -63,7 +71,7 @@ limitations under the License. } .mx_RoomTile_badge { - background-color: #80cef4; + background-color: #76cfa6; color: #fff; border-radius: 26px; font-weight: 400; @@ -75,6 +83,7 @@ limitations under the License. } */ +/* .mx_RoomTile_badge { background-color: #ff0064; border: 3px solid #fff; @@ -85,20 +94,48 @@ limitations under the License. right: 9px; bottom: 3px; } +*/ -.mx_RoomTile_unread, -.mx_RoomTile_highlight, -.mx_RoomTile_invited -{ - font-weight: bold; - color: #000; +.mx_RoomTile_badge { + background-color: #ff0064; + width: 4px; + position: absolute; + left: 0px; + top: 5px; + bottom: 5px; +} + +.mx_RoomTile_unreadNotify .mx_RoomTile_badge { + background-color: #454545; } -.mx_RoomTile_selected { - background-color: #f3f8fa; - color: #80cef4; +.mx_RoomTile_highlight .mx_RoomTile_badge { + background-color: #ff0064; +} + +.mx_RoomTile_unread, +.mx_RoomTile_highlight { font-weight: bold; } +.mx_RoomTile_selected .mx_RoomTile_name { + 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; + background-position: right center; +} + +.mx_RoomTile_arrow { + position: absolute; + right: 0px; +} + .mx_RoomTile:hover { } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/SearchableEntityList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/SearchableEntityList.css new file mode 100644 index 00000000..0d7639bf --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/SearchableEntityList.css @@ -0,0 +1,80 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SearchableEntityList { + display: flex; + display: -webkit-flex; + + flex-direction: column; + -webkit-flex-direction: column; +} + +.mx_SearchableEntityList_query { + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; + border-radius: 3px; + border: 1px solid #f0f0f0; + padding: 9px; + color: #454545; + margin-left: 3px; + font-size: 15px; + margin-bottom: 8px; + width: 180px; +} + +.mx_SearchableEntityList_query::-moz-placeholder { + color: #454545; + opacity: 0.5; + font-size: 12px; +} + +.mx_SearchableEntityList_query::-webkit-input-placeholder { + color: #454545; + opacity: 0.5; + font-size: 12px; +} + +.mx_SearchableEntityList_listWrapper { + flex: 1; + -webkit-flex: 1; + + overflow-y: auto; +} + +.mx_SearchableEntityList_list { + display: table; + table-layout: fixed; + width: 100%; +} + +.mx_SearchableEntityList_list .mx_EntityTile_chevron { + display: none; +} + +.mx_SearchableEntityList_hrWrapper { + width: 100%; + flex: 0 0 auto; + -webkit-flex: 0 0 auto; +} + +.mx_SearchableEntityList hr { + height: 1px; + border: 0px; + color: #e1dddd; + background-color: #e1dddd; + margin-right: 15px; + margin-top: 11px; + margin-bottom: 11px; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/TabCompleteBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/TabCompleteBar.css new file mode 100644 index 00000000..e0d0965e --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/TabCompleteBar.css @@ -0,0 +1,51 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_TabCompleteBar { + overflow: hidden; +} + +.mx_TabCompleteBar_item { + display: inline-block; + margin-right: 15px; + cursor: pointer; +} + +.mx_TabCompleteBar_command { + margin-right: 8px; + background-color: #76CFA6; + padding-left: 8px; + padding-right: 8px; + padding-top: 2px; + padding-bottom: 2px; + margin-bottom: 6px; + border-radius: 30px; +} + +.mx_TabCompleteBar_command .mx_TabCompleteBar_text { + opacity: 1.0; + color: #fff; +} + +.mx_TabCompleteBar_item img { + margin-right: 8px; + vertical-align: middle; +} + +.mx_TabCompleteBar_text { + color: #4a4a4a; + opacity: 0.5; +} diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/TopUnreadMessagesBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/TopUnreadMessagesBar.css new file mode 100644 index 00000000..35f68399 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/TopUnreadMessagesBar.css @@ -0,0 +1,45 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_TopUnreadMessagesBar { + margin: auto; /* centre horizontally */ + max-width: 960px; + padding-top: 5px; + padding-bottom: 5px; + border-bottom: 1px solid #eee; +} + +.mx_TopUnreadMessagesBar_scrollUp { + display: inline; + cursor: pointer; +} + +.mx_TopUnreadMessagesBar_scrollUp img { + padding-left: 10px; + padding-right: 31px; + vertical-align: middle; +} + +.mx_TopUnreadMessagesBar_scrollUp span { + opacity: 0.5; +} + +.mx_TopUnreadMessagesBar_close { + float: right; + padding-right: 14px; + padding-top: 3px; + cursor: pointer; +} diff --git a/src/skins/vector/css/molecules/voip/CallView.css b/src/skins/vector/css/matrix-react-sdk/views/voip/CallView.css similarity index 93% rename from src/skins/vector/css/molecules/voip/CallView.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/CallView.css index 01cdb45a..179fd294 100644 --- a/src/skins/vector/css/molecules/voip/CallView.css +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/CallView.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/skins/vector/css/molecules/voip/IncomingCallbox.css b/src/skins/vector/css/matrix-react-sdk/views/voip/IncomingCallbox.css similarity index 84% rename from src/skins/vector/css/molecules/voip/IncomingCallbox.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/IncomingCallbox.css index 2c57a327..5cf62091 100644 --- a/src/skins/vector/css/molecules/voip/IncomingCallbox.css +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/IncomingCallbox.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,14 +16,15 @@ limitations under the License. .mx_IncomingCallBox { text-align: center; - border: 1px solid #a9dbf4; + border: 1px solid #a4a4a4; border-radius: 8px; background-color: #fff; - position: absolute; + position: fixed; z-index: 1000; - left: 235px; - top: 155px; padding: 6px; + margin-top: -3px; + margin-left: -20px; + width: 200px; } .mx_IncomingCallBox_chevron { @@ -39,14 +40,15 @@ limitations under the License. } .mx_IncomingCallBox_buttons { - display: table-row; + display: flex; + display: -webkit-flex; } .mx_IncomingCallBox_buttons_cell { vertical-align: middle; - display: table-cell; padding: 6px; - width: 50%; + flex: 1; + -webkit-flex: 1; } .mx_IncomingCallBox_buttons_decline, @@ -57,6 +59,7 @@ limitations under the License. line-height: 36px; border-radius: 36px; color: #fff; + margin: auto; } .mx_IncomingCallBox_buttons_decline { diff --git a/src/skins/vector/css/molecules/voip/VideoView.css b/src/skins/vector/css/matrix-react-sdk/views/voip/VideoView.css similarity index 82% rename from src/skins/vector/css/molecules/voip/VideoView.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/VideoView.css index b47039c3..8f23ef6b 100644 --- a/src/skins/vector/css/molecules/voip/VideoView.css +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/VideoView.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -31,9 +31,15 @@ limitations under the License. } .mx_VideoView_localVideoFeed { - width: 20%; + width: 25%; + height: 25%; position: absolute; - left: 16px; - bottom: 28px; + left: 10px; + bottom: 10px; z-index: 100; +} + +.mx_VideoView_localVideoFeed video { + width: auto; + height: 100%; } \ No newline at end of file diff --git a/src/skins/vector/css/molecules/EventTile.css b/src/skins/vector/css/molecules/EventTile.css deleted file mode 100644 index 1cd2fa46..00000000 --- a/src/skins/vector/css/molecules/EventTile.css +++ /dev/null @@ -1,112 +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. -*/ - -.mx_EventTile { - max-width: 100%; - clear: both; - margin-top: 32px; - margin-left: 56px; -} - -.mx_EventTile_avatar { - padding-left: 12px; - padding-right: 12px; - margin-left: -64px; - margin-top: -7px; - float: left; -} - -.mx_EventTile_avatar img { - background-color: #dbdbdb; - border-radius: 20px; - border: 0px; -} - -.mx_EventTile_continuation { - margin-top: 8px ! important; -} - -.mx_EventTile .mx_SenderProfile { - color: #454545; - opacity: 0.5; - font-size: 14px; - margin-bottom: 4px; - display: block; -} - -.mx_EventTile .mx_MessageTimestamp { - color: #454545; - opacity: 0.5; - font-size: 14px; - float: right; -} - -.mx_EventTile_content { - padding-right: 100px; - display: block; -} - -.mx_EventTile_notice .mx_MessageTile_content { - opacity: 0.5; -} - -.mx_EventTile_sending { - color: #ddd; -} - -.mx_EventTile_notSent { - color: #f11; -} - -.mx_EventTile_highlight { - color: #FF0064; -} - -.mx_EventTile_msgOption { - float: right; -} - -.mx_MessageTimestamp { - display: none; -} - -.mx_EventTile_last .mx_MessageTimestamp { - display: block; -} - -.mx_EventTile:hover .mx_MessageTimestamp { - display: block; -} - -.mx_EventTile_editButton { - float: right; - display: none; - border: 0px; - outline: none; - margin-right: 3px; -} - -.mx_EventTile:hover .mx_EventTile_editButton { - display: inline-block; -} - -.mx_EventTile.menu .mx_EventTile_editButton { - display: inline-block; -} - -.mx_EventTile.menu .mx_MessageTimestamp { - display: inline-block; -} diff --git a/src/skins/vector/css/molecules/MemberTile.css b/src/skins/vector/css/molecules/MemberTile.css deleted file mode 100644 index faae142a..00000000 --- a/src/skins/vector/css/molecules/MemberTile.css +++ /dev/null @@ -1,134 +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. -*/ - -.mx_MemberTile { - display: table-row; - height: 49px; - position: relative; -} - -.mx_MemberTile_avatar { - display: table-cell; - padding-left: 14px; - padding-right: 12px; - padding-top: 3px; - padding-bottom: 3px; - vertical-align: middle; - width: 40px; - height: 40px; - position: relative; -} - -.mx_MemberTile_inviteTile { - cursor: pointer; -} - -.mx_MemberTile_inviteEditing { - display: initial ! important; -} - -.mx_MemberTile_inviteEditing .mx_MemberTile_avatar { - display: none; -} - -.mx_MemberTile_inviteEditing .mx_MemberTile_name { - width: 200px; -} - -.mx_MemberTile_inviteEditing .mx_MemberTile_name input { - border-radius: 3px; - border: 1px solid #c7c7c7; - font-weight: 300; - font-size: 14px; - padding: 9px; - margin-top: 6px; - margin-left: 14px; -} - -.mx_MemberTile_power { - position: absolute; - width: 48px; - height: 48px; - left: 10px; - top: -1px; -} - -.mx_MemberTile_name { - display: table-cell; - vertical-align: middle; - overflow: hidden; - text-overflow: ellipsis; -} - -.mx_MemberTile_details { - display: table-cell; - padding-right: 14px; - vertical-align: middle; -} - -.mx_MemberTile_hover { - background-color: #f0f0f0; - font-size: 12px; - color: #747474; -} - -.mx_MemberTile_userId { - font-weight: bold; - overflow: hidden; - text-overflow: ellipsis; -} - -.mx_MemberTile_leave { - cursor: pointer; - margin-top: 8px; - margin-right: -4px; - margin-left: 6px; - float: right; -} - -/* -.mx_MemberTile_nameWrapper { - display: table-cell; - vertical-align: middle; - overflow: hidden; - text-overflow: ellipsis; -} - -.mx_MemberTile_nameSpan { -} -*/ - -.mx_MemberTile_unavailable .mx_MemberTile_avatar, -.mx_MemberTile_unavailable .mx_MemberTile_name, -.mx_MemberTile_unavailable .mx_MemberTile_nameSpan -{ - opacity: 0.66; -} - -.mx_MemberTile_offline .mx_MemberTile_avatar, -.mx_MemberTile_offline .mx_MemberTile_name, -.mx_MemberTile_offline .mx_MemberTile_nameSpan -{ - 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/RoomHeader.css b/src/skins/vector/css/molecules/RoomHeader.css deleted file mode 100644 index f7adb618..00000000 --- a/src/skins/vector/css/molecules/RoomHeader.css +++ /dev/null @@ -1,173 +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. -*/ - -.mx_RoomHeader { -} - -.mx_RoomHeader_wrapper { - max-width: 720px; - margin: auto; - height: 88px; - border-bottom: 1px solid #a8dbf3; - - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; -} - -.mx_RoomHeader_leftRow { - height: 48px; - margin-top: 18px; - - -webkit-box-ordinal-group: 1; - -moz-box-ordinal-group: 1; - -ms-flex-order: 1; - -webkit-order: 1; - order: 1; - - -webkit-flex: 1; - flex: 1; -} - -.mx_RoomHeader_textButton { - height: 48px; - margin-top: 18px; - background-color: #80cef4; - border-radius: 48px; - margin-right: 8px; - color: #fff; - line-height: 48px; - text-align: center; - - -webkit-box-ordinal-group: 2; - -moz-box-ordinal-group: 2; - -ms-flex-order: 2; - -webkit-order: 2; - order: 2; - - cursor: pointer; - -/* - -webkit-flex: 0 0 90px; - flex: 0 0 90px; -*/ - padding-left: 12px; - padding-right: 12px; - } - -.mx_RoomHeader_rightRow { - height: 48px; - margin-top: 18px; - background-color: #fff; - border-radius: 48px; - border: 1px solid #a9dbf4; - - -webkit-box-ordinal-group: 3; - -moz-box-ordinal-group: 3; - -ms-flex-order: 3; - -webkit-order: 3; - order: 3; -} - -.mx_RoomHeader_info { - display: table-cell; - height: 48px; - vertical-align: middle; -} - -.mx_RoomHeader_simpleHeader { - line-height: 88px; - color: #80cef4; - font-weight: 400; - font-size: 20px; - overflow: hidden; - text-overflow: ellipsis; -} - -.mx_RoomHeader_name { - vertical-align: middle; - height: 28px; - color: #80cef4; - font-weight: 400; - font-size: 20px; - padding-left: 16px; - padding-right: 16px; - text-overflow: ellipsis; -} - -.mx_RoomHeader_nameEditing { - padding-left: 16px; - padding-right: 16px; - margin-top: -5px; -} - -.mx_RoomHeader_name input, .mx_RoomHeader_nameInput { - border-radius: 3px; - width: 260px; - border: 1px solid #c7c7c7; - font-weight: 300; - font-size: 14px; - padding: 9px; -} - -.mx_RoomHeader_nameInput { - margin-top: 6px; -} - -.mx_RoomHeader_topic { - vertical-align: bottom; - float: left; - max-height: 38px; - color: #70b5d7; - font-weight: 300; - padding-left: 16px; - padding-right: 16px; - overflow: hidden; - text-overflow: ellipsis; -} - -.mx_RoomHeader_avatar { - display: table-cell; - width: 48px; - height: 50px; - vertical-align: middle; -} - -.mx_RoomHeader_avatar img { - border-radius: 24px; -} - -.mx_RoomHeader_button { - height: 48px; - display: table-cell; - vertical-align: middle; - padding-left: 8px; - padding-right: 8px; -} - -.mx_RoomHeader_button img { - cursor: pointer; -} - -.mx_RoomHeader_voipButton { - display: table-cell; -} - -.mx_RoomHeader_voipButtons { - margin-top: 18px; -} \ No newline at end of file diff --git a/src/skins/vector/css/molecules/RoomSettings.css b/src/skins/vector/css/molecules/RoomSettings.css deleted file mode 100644 index 3a66f0e9..00000000 --- a/src/skins/vector/css/molecules/RoomSettings.css +++ /dev/null @@ -1,70 +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. -*/ - -.mx_RoomSettings { - max-height: 250px; - padding-top: 12px; -} - -.mx_RoomSettings_settings { - display: table; - margin: 5px 0; -} - -.mx_RoomSettings_settings > div { - display: table-row; -} - -.mx_RoomSettings_settings > div > * { - display: table-cell; - - margin: 0 10px; -} - -.mx_RoomSettings input, -.mx_RoomSettings textarea { - border-radius: 3px; - border: 1px solid #c7c7c7; - font-weight: 300; - font-size: 14px; - padding: 9px; - margin-top: 6px; -} - -.mx_RoomSettings_description { - width: 330px; -} - -.mx_RoomSettings_buttons { - text-align: right; - margin-bottom: 16px; -} - -.mx_RoomSettings_button { - display: inline; - border: 0px; - height: 36px; - border-radius: 36px; - font-weight: 400; - font-size: 16px; - color: #fff; - background-color: #80cef4; - width: auto; - margin: auto; - padding: 6px; - padding-left: 1em; - padding-right: 1em; -} \ No newline at end of file diff --git a/src/skins/vector/css/molecules/SenderProfile.css b/src/skins/vector/css/molecules/SenderProfile.css deleted file mode 100644 index 45db913e..00000000 --- a/src/skins/vector/css/molecules/SenderProfile.css +++ /dev/null @@ -1,20 +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. -*/ - -.mx_SenderProfile_zalgo { - font-family: Helvetica, Arial, Sans-Serif; - display: table-row ! important; -} diff --git a/src/skins/vector/css/organisms/UserSettings.css b/src/skins/vector/css/organisms/UserSettings.css deleted file mode 100644 index b69399b7..00000000 --- a/src/skins/vector/css/organisms/UserSettings.css +++ /dev/null @@ -1,21 +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. -*/ - -.mx_UserSettings { - width: 720px; - margin-left: auto; - margin-right: auto; -} diff --git a/src/skins/vector/css/organisms/ViewSource.css b/src/skins/vector/css/organisms/ViewSource.css deleted file mode 100644 index ae61ae58..00000000 --- a/src/skins/vector/css/organisms/ViewSource.css +++ /dev/null @@ -1,3 +0,0 @@ -.mx_ViewSource pre { - text-align: left; -} diff --git a/src/skins/vector/css/vector-web/structures/CompatibilityPage.css b/src/skins/vector/css/vector-web/structures/CompatibilityPage.css new file mode 100644 index 00000000..f3f032c9 --- /dev/null +++ b/src/skins/vector/css/vector-web/structures/CompatibilityPage.css @@ -0,0 +1,19 @@ +.mx_CompatibilityPage { + width: 100%; + height: 100%; + background-color: #e55; +} + +.mx_CompatibilityPage_box { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 500px; + height: 300px; + border: 1px solid; + padding: 10px; + background-color: #fcc; +} \ No newline at end of file diff --git a/src/skins/vector/css/organisms/LeftPanel.css b/src/skins/vector/css/vector-web/structures/LeftPanel.css similarity index 77% rename from src/skins/vector/css/organisms/LeftPanel.css rename to src/skins/vector/css/vector-web/structures/LeftPanel.css index 0e7e077e..68f689b6 100644 --- a/src/skins/vector/css/organisms/LeftPanel.css +++ b/src/skins/vector/css/vector-web/structures/LeftPanel.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34,16 +34,21 @@ limitations under the License. cursor: pointer; } -.mx_LeftPanel .mx_RoomList { +.mx_LeftPanel_callView { + +} + +.mx_LeftPanel .mx_RoomList_scrollbar { -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; -webkit-order: 1; order: 1; - overflow-y: auto; -webkit-flex: 1 1 0; flex: 1 1 0; + + overflow-y: auto; } .mx_LeftPanel .mx_BottomLeftMenu { @@ -53,17 +58,23 @@ limitations under the License. -webkit-order: 3; order: 3; - -webkit-flex: 0 0 170px; - flex: 0 0 170px; + -webkit-flex: 0 0 140px; + flex: 0 0 140px; - border-top: 1px solid #f3f8fa; + background-color: rgba(118,207,166,0.2); } .mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile { - color: #378bb4; + color: #454545; } .mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options { - margin-top: 12px; + 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/RightPanel.css b/src/skins/vector/css/vector-web/structures/RightPanel.css similarity index 61% rename from src/skins/vector/css/organisms/RightPanel.css rename to src/skins/vector/css/vector-web/structures/RightPanel.css index 30ef4726..12e101a3 100644 --- a/src/skins/vector/css/organisms/RightPanel.css +++ b/src/skins/vector/css/vector-web/structures/RightPanel.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,32 +33,57 @@ limitations under the License. -webkit-order: 1; order: 1; - -webkit-flex: 0 0 66px; - flex: 0 0 66px; + -webkit-flex: 0 0 83px; + flex: 0 0 83px; } /** Fixme - factor this out with the main header **/ .mx_RightPanel_headerButtonGroup { - margin-top: 18px; - height: 48px; - float: right; + margin-top: 32px; + float: left; background-color: #fff; - border-radius: 48px; - border: 1px solid #a9dbf4; - margin-right: 22px; + margin-left: -4px; } .mx_RightPanel_headerButton { cursor: pointer; - height: 48px; display: table-cell; vertical-align: middle; - padding-left: 8px; - padding-right: 8px; + padding-left: 15px; + padding-right: 15px; + position: relative; } -.mx_RightPanel .mx_MemberList { +.mx_RightPanel_headerButton object { + pointer-events: none; +} + +.mx_RightPanel_headerButton_highlight { + position: absolute; + bottom: -2px; + left: 10px; + width: 25px; + height: 4px; + background-color: #76cfa6; +} + +.mx_RightPanel_headerButton_badge { + position: absolute; + top: 4px; + left: 28px; + font-size: 12px; + background-color: #76cfa6; + color: #fff; + font-weight: bold; + border-radius: 20px; + padding-left: 4px; + padding-right: 4px; + padding-top: 0px; +} + +.mx_RightPanel .mx_MemberList, +.mx_RightPanel .mx_MemberInfo { -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; diff --git a/src/skins/vector/css/organisms/RoomDirectory.css b/src/skins/vector/css/vector-web/structures/RoomDirectory.css similarity index 62% rename from src/skins/vector/css/organisms/RoomDirectory.css rename to src/skins/vector/css/vector-web/structures/RoomDirectory.css index 21985a25..a61e9e43 100644 --- a/src/skins/vector/css/organisms/RoomDirectory.css +++ b/src/skins/vector/css/vector-web/structures/RoomDirectory.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,8 @@ limitations under the License. */ .mx_RoomDirectory { - width: 720px; + max-width: 960px; + width: 100%; margin-left: auto; margin-right: auto; margin-bottom: 12px; @@ -50,7 +51,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; @@ -63,37 +64,61 @@ limitations under the License. } .mx_RoomDirectory_table { + font-size: 14px; + color: #4a4a4a; width: 100%; text-align: left; table-layout: fixed; } -.mx_RoomDirectory_table th { - font-weight: 400; - font-size: 12px; +.mx_RoomDirectory_roomAvatar { + width: 24px; + padding-left: 12px; + padding-right: 24px; + vertical-align: top; } -.mx_RoomDirectory_table tbody { +.mx_RoomDirectory_roomDescription { + padding-bottom: 16px; +} + +.mx_RoomDirectory_name { + display: inline-block; + font-weight: 600; +} + +.mx_RoomDirectory_perms { + display: inline-block; +} + +.mx_RoomDirectory_perm { + display: inline; + padding-left: 5px; + padding-right: 5px; + height: 15px; + border-radius: 11px; + background-color: #eaf5f0; + text-transform: uppercase; + font-weight: 600; + font-size: 11px; + color: #61c295; +} + +.mx_RoomDirectory_topic { + cursor: initial; +} + +.mx_RoomDirectory_alias { + font-size: 12px; + color: #b3b3b3; +} + +.mx_RoomDirectory_roomMemberCount { + text-align: right; + width: 100px; +} + +.mx_RoomDirectory_table tr { + padding-bottom: 10px; cursor: pointer; } - -.mx_RoomDirectory_table td { - font-weight: 300; - font-size: 16px; - overflow-x: hidden; - text-overflow: ellipsis; -} - -.mx_RoomDirectory_table .mx_RoomDirectory_name { - font-weight: 400; -} - -.mx_RoomDirectory_table .mx_RoomDirectory_topic { - font-weight: 400; - font-size: 12px; -} - -.mx_RoomDirectory_table td, -.mx_RoomDirectory_table th, { - padding: 6px; -} \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/structures/RoomSubList.css b/src/skins/vector/css/vector-web/structures/RoomSubList.css new file mode 100644 index 00000000..d385397b --- /dev/null +++ b/src/skins/vector/css/vector-web/structures/RoomSubList.css @@ -0,0 +1,41 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomSubList { + display: table; + table-layout: fixed; + width: 100%; +} + +.mx_RoomSubList_label { + text-transform: uppercase; + color: #3d3b39; + font-weight: 600; + font-size: 13px; + padding-left: 12px; + padding-right: 12px; + margin-top: 8px; + margin-bottom: 4px; +} + +.mx_RoomSubList_chevron { + padding-left: 5px; + pointer-events: none; +} + +.collapsed .mx_RoomSubList_chevron { + padding-left: 13px; +} diff --git a/src/skins/vector/css/atoms/MemberAvatar.css b/src/skins/vector/css/vector-web/structures/ViewSource.css similarity index 76% rename from src/skins/vector/css/atoms/MemberAvatar.css rename to src/skins/vector/css/vector-web/structures/ViewSource.css index 6422df79..c4017e45 100644 --- a/src/skins/vector/css/atoms/MemberAvatar.css +++ b/src/skins/vector/css/vector-web/structures/ViewSource.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberAvatar { - z-index: 20; - border-radius: 20px; - background-color: #dbdbdb; +.mx_ViewSource pre { + text-align: left; + font-size: 12px; + padding: 0.5em 1em 0.5em 1em; + word-wrap: break-word; } - diff --git a/src/skins/vector/css/vector-web/views/elements/ImageView.css b/src/skins/vector/css/vector-web/views/elements/ImageView.css new file mode 100644 index 00000000..79d0b1af --- /dev/null +++ b/src/skins/vector/css/vector-web/views/elements/ImageView.css @@ -0,0 +1,150 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* This has got to be the most fragile piece of CSS ever written. + But empirically it works on Chrome/FF/Safari + */ + +.mx_ImageView { + display: -webkit-flex; + display: flex; + width: 100%; + height: 100%; + -webkit-align-items: center; + align-items: center; +} + +.mx_ImageView_lhs { + -webkit-box-ordinal-group: 1; + order: 1; + -webkit-flex: 1; + flex: 1 1 10%; + min-width: 60px; + /* + background-color: #080; + height: 20px; + */ +} + +.mx_ImageView_content { + -webkit-box-ordinal-group: 2; + order: 2; + /* min-width hack needed for FF */ + min-width: 0px; + height: 90%; + -webkit-flex: 15; + flex: 15 15 0; + + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + -webkit-justify-content: center; + align-items: center; + justify-content: center; +} + +.mx_ImageView_content img { + max-width: 100%; + /* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */ + max-height: 100%; + /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ + object-fit: contain; + /* background-image: url('img/trans.png'); */ + pointer-events: all; +} + +.mx_ImageView_labelWrapper { + position: absolute; + top: 0px; + height: 100%; + overflow: auto; + pointer-events: all; +} + +.mx_ImageView_label { + text-align: left; + display: flex; + display: -webkit-flex; + justify-content: center; + -webkit-justify-content: center; + flex-direction: column; + -webkit-flex-direction: column; + padding-left: 60px; + padding-right: 60px; + min-height: 100%; + color: #fff; +} + +.mx_ImageView_cancel { + position: absolute; + top: 0px; + right: 0px; + padding: 35px; + cursor: pointer; +} + +.mx_ImageView_name { + font-size: 18px; + margin-bottom: 6px; +} + +.mx_ImageView_metadata { + font-size: 15px; + opacity: 0.5; +} + +.mx_ImageView_download { + display: table; + margin-top: 24px; + margin-bottom: 6px; + border-radius: 5px; + background-color: #454545; + font-size: 14px; + padding: 9px; + border: 1px solid #fff; +} + +.mx_ImageView_size { + font-size: 11px; +} + +.mx_ImageView_link { + color: #fff ! important; + text-decoration: none ! important; +} + +.mx_ImageView_button { + font-size: 15px; + opacity: 0.5; + margin-top: 18px; + cursor: pointer; +} + +.mx_ImageView_shim { + height: 30px; +} + +.mx_ImageView_rhs { + -webkit-box-ordinal-group: 3; + order: 3; + -webkit-flex: 1; + flex: 1 1 10%; + min-width: 300px; + /* + background-color: #800; + height: 20px; + */ +} diff --git a/src/skins/vector/css/organisms/RoomList.css b/src/skins/vector/css/vector-web/views/elements/Spinner.css similarity index 62% rename from src/skins/vector/css/organisms/RoomList.css rename to src/skins/vector/css/vector-web/views/elements/Spinner.css index 21cb7812..3831cc4c 100644 --- a/src/skins/vector/css/organisms/RoomList.css +++ b/src/skins/vector/css/vector-web/views/elements/Spinner.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,18 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RoomList { -} - -.mx_RoomList_recents { - margin-top: -12px; - display: table; - table-layout: fixed; +.mx_Spinner { + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + -webkit-justify-content: center; + align-items: center; + justify-content: center; width: 100%; + height: 100%; + flex: 1; + -webkit-flex: 1; } -.mx_RoomList h2 { - padding-left: 16px; - padding-right: 16px; - padding-bottom: 10px; +.mx_MatrixChat_middlePanel .mx_Spinner { + height: auto; } \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/views/globals/GuestWarningBar.css b/src/skins/vector/css/vector-web/views/globals/GuestWarningBar.css new file mode 100644 index 00000000..717d75af --- /dev/null +++ b/src/skins/vector/css/vector-web/views/globals/GuestWarningBar.css @@ -0,0 +1,41 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_GuestWarningBar { + background-color: #76cfa6; + color: #fff; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + align-items: center; +} + +.mx_GuestWarningBar_warning { + margin-left: 16px; + margin-right: 8px; + margin-top: -2px; +} + +.mx_GuestWarningBar a { + color: #fff ! important; + text-decoration: underline ! important; + cursor: pointer; +} + diff --git a/src/skins/vector/css/vector-web/views/globals/MatrixToolbar.css b/src/skins/vector/css/vector-web/views/globals/MatrixToolbar.css new file mode 100644 index 00000000..b5e91021 --- /dev/null +++ b/src/skins/vector/css/vector-web/views/globals/MatrixToolbar.css @@ -0,0 +1,54 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MatrixToolbar { + background-color: #76cfa6; + color: #fff; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + align-items: center; +} + +.mx_MatrixToolbar_warning { + margin-left: 16px; + margin-right: 8px; + margin-top: -2px; +} + +.mx_MatrixToolbar_link +{ + color: #fff ! important; + text-decoration: underline ! important; + cursor: pointer; +} + +.mx_MatrixToolbar_close { + -webkit-flex: 1; + flex: 1; + cursor: pointer; + text-align: right; +} + +.mx_MatrixToolbar_close img { + display: block; + float: right; + margin-right: 10px; +} diff --git a/src/skins/vector/css/atoms/MessageTimestamp.css b/src/skins/vector/css/vector-web/views/messages/MessageTimestamp.css similarity index 93% rename from src/skins/vector/css/atoms/MessageTimestamp.css rename to src/skins/vector/css/vector-web/views/messages/MessageTimestamp.css index b3c68509..e21189c5 100644 --- a/src/skins/vector/css/atoms/MessageTimestamp.css +++ b/src/skins/vector/css/vector-web/views/messages/MessageTimestamp.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/skins/vector/css/molecules/MemberInfo.css b/src/skins/vector/css/vector-web/views/messages/SenderProfile.css similarity index 93% rename from src/skins/vector/css/molecules/MemberInfo.css rename to src/skins/vector/css/vector-web/views/messages/SenderProfile.css index 52c48a79..060709b8 100644 --- a/src/skins/vector/css/molecules/MemberInfo.css +++ b/src/skins/vector/css/vector-web/views/messages/SenderProfile.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,4 +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. */ - diff --git a/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css b/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css new file mode 100644 index 00000000..7ad5e893 --- /dev/null +++ b/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css @@ -0,0 +1,61 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomDropTarget { + font-size: 13px; + margin-left: 10px; + margin-right: 15px; + padding-top: 5px; + padding-bottom: 5px; + border: 1px dashed #76cfa6; + color: #454545; + background-color: rgba(255,255,255,0.5); + border-radius: 4px; +} + +.collapsed .mx_RoomDropTarget { + margin-right: 10px; +} + +.mx_RoomDropTarget_placeholder { + padding-top: 1px; + padding-bottom: 1px; +} + +.mx_RoomDropTarget_avatar { + background-color: #fff; + border-radius: 24px; + width: 24px; + height: 24px; + float: left; + margin-left: 7px; + margin-right: 7px; +} + +.mx_RoomDropTarget_label { + position: relative; + margin-top: 3px; + line-height: 21px; + z-index: 1; +} + +.collapsed .mx_RoomDropTarget_avatar { + float: none; +} + +.collapsed .mx_RoomDropTarget_label { + display: none; +} diff --git a/src/skins/vector/css/molecules/RoomTooltip.css b/src/skins/vector/css/vector-web/views/rooms/RoomTooltip.css similarity index 90% rename from src/skins/vector/css/molecules/RoomTooltip.css rename to src/skins/vector/css/vector-web/views/rooms/RoomTooltip.css index f45970fe..3aec0fa7 100644 --- a/src/skins/vector/css/molecules/RoomTooltip.css +++ b/src/skins/vector/css/vector-web/views/rooms/RoomTooltip.css @@ -1,5 +1,5 @@ /* -Copyright 2015 OpenMarket Ltd +Copyright 2015, 2016 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,11 +17,10 @@ limitations under the License. .mx_RoomTooltip { display: none; position: fixed; - border: 1px solid #a9dbf4; + border: 1px solid #a4a4a4; border-radius: 8px; background-color: #fff; z-index: 1000; - margin-top: 6px; left: 64px; padding: 6px; } diff --git a/src/skins/vector/css/vector-web/views/rooms/SearchBar.css b/src/skins/vector/css/vector-web/views/rooms/SearchBar.css new file mode 100644 index 00000000..37239a95 --- /dev/null +++ b/src/skins/vector/css/vector-web/views/rooms/SearchBar.css @@ -0,0 +1,86 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SearchBar { + padding-top: 5px; + padding-bottom: 5px; + display: flex; + display: -webkit-flex; + align-items: center; + -webkit-align-items: center; +} + +.mx_SearchBar_input { + display: inline block; + border-radius: 3px 0px 0px 3px; + border: 1px solid #f0f0f0; + font-size: 15px; + padding: 9px; + padding-left: 11px; + width: auto; + flex: 1 1 0; + -webkit-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.1; } + 100% { opacity: 1.0; } +} + +.mx_SearchBar_searching img { + animation: pulsate 0.5s ease-out; + animation-iteration-count: infinite; +} + +.mx_SearchBar_button { + display: inline; + border: 0px; + border-radius: 36px; + font-weight: 400; + font-size: 15px; + color: #fff; + background-color: #76cfa6; + width: auto; + margin: auto; + margin-left: 7px; + padding-top: 6px; + padding-bottom: 4px; + padding-left: 24px; + padding-right: 24px; + cursor: pointer; +} + +.mx_SearchBar_unselected { + background-color: #fff; + color: #76CFA6; + border: #76CFA6 1px solid; +} + +.mx_SearchBar_cancel { + padding-left: 14px; + padding-right: 14px; + cursor: pointer; +} diff --git a/src/skins/vector/css/vector-web/views/settings/Notifications.css b/src/skins/vector/css/vector-web/views/settings/Notifications.css new file mode 100644 index 00000000..f0446e43 --- /dev/null +++ b/src/skins/vector/css/vector-web/views/settings/Notifications.css @@ -0,0 +1,62 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_UserNotifSettings_tableRow +{ + display: table-row; +} + +.mx_UserNotifSettings_inputCell { + display: table-cell; + padding-bottom: 21px; + padding-right: 8px; + width: 16px; +} + +.mx_UserNotifSettings_labelCell +{ + padding-bottom: 21px; + width: 270px; + display: table-cell; +} + +.mx_UserNotifSettings_pushRulesTableWrapper { + padding-bottom: 21px; +} + +.mx_UserNotifSettings_pushRulesTable { + width: 100%; + table-layout: fixed; +} + +.mx_UserNotifSettings_pushRulesTable thead { + font-weight: bold; + font-size: 15px; +} + +.mx_UserNotifSettings_pushRulesTable tbody th { + font-weight: 400; + font-size: 15px; +} + +.mx_UserNotifSettings_pushRulesTable tbody th:first-child { + text-align: left; +} + +.mx_UserNotifSettings_keywords { + cursor: pointer; + color: #76cfa6; +} diff --git a/src/skins/vector/fonts/OpenSans.css b/src/skins/vector/fonts/OpenSans.css new file mode 100644 index 00000000..cba0081a --- /dev/null +++ b/src/skins/vector/fonts/OpenSans.css @@ -0,0 +1,24 @@ +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(opensans/v13/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url(opensans/v13/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; +} \ No newline at end of file diff --git a/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf new file mode 100644 index 00000000..bffc5fcf Binary files /dev/null and b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf differ diff --git a/src/skins/vector/fonts/opensans/v13/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2 b/src/skins/vector/fonts/opensans/v13/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2 new file mode 100644 index 00000000..4001c52a Binary files /dev/null and b/src/skins/vector/fonts/opensans/v13/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2 differ diff --git a/src/skins/vector/fonts/opensans/v13/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 b/src/skins/vector/fonts/opensans/v13/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 new file mode 100644 index 00000000..402dfd77 Binary files /dev/null and b/src/skins/vector/fonts/opensans/v13/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 differ diff --git a/src/skins/vector/fonts/opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2 b/src/skins/vector/fonts/opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2 new file mode 100644 index 00000000..51c75cad Binary files /dev/null and b/src/skins/vector/fonts/opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2 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 00000000..89ce23b4 Binary files /dev/null and b/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf differ diff --git a/src/skins/vector/img/76cfa6.png b/src/skins/vector/img/76cfa6.png new file mode 100644 index 00000000..de1ea60d Binary files /dev/null and b/src/skins/vector/img/76cfa6.png differ diff --git a/src/skins/vector/img/admin.svg b/src/skins/vector/img/admin.svg new file mode 100644 index 00000000..7ea74593 --- /dev/null +++ b/src/skins/vector/img/admin.svg @@ -0,0 +1,17 @@ + + + + icons_owner + Created with sketchtool. + + + + + + + + + + + + diff --git a/src/skins/vector/img/call.png b/src/skins/vector/img/call.png new file mode 100644 index 00000000..a7805e05 Binary files /dev/null and b/src/skins/vector/img/call.png differ diff --git a/src/skins/vector/img/call.svg b/src/skins/vector/img/call.svg new file mode 100644 index 00000000..f528f9a2 --- /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/camera.svg b/src/skins/vector/img/camera.svg new file mode 100644 index 00000000..6519496f --- /dev/null +++ b/src/skins/vector/img/camera.svg @@ -0,0 +1,12 @@ + + + + icon_camera + Created with Sketch. + + + + + + + diff --git a/src/skins/vector/img/cancel-black.png b/src/skins/vector/img/cancel-black.png new file mode 100644 index 00000000..87dcfd41 Binary files /dev/null and b/src/skins/vector/img/cancel-black.png differ diff --git a/src/skins/vector/img/cancel-black2.png b/src/skins/vector/img/cancel-black2.png new file mode 100644 index 00000000..a928c61b Binary files /dev/null and b/src/skins/vector/img/cancel-black2.png differ diff --git a/src/skins/vector/img/cancel-small.svg b/src/skins/vector/img/cancel-small.svg new file mode 100644 index 00000000..e4c8cafc --- /dev/null +++ b/src/skins/vector/img/cancel-small.svg @@ -0,0 +1,13 @@ + + + + Line + Line + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/cancel-white.svg b/src/skins/vector/img/cancel-white.svg new file mode 100644 index 00000000..65e14c2f --- /dev/null +++ b/src/skins/vector/img/cancel-white.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/cancel.png b/src/skins/vector/img/cancel.png index c963a4b7..2bda8ff5 100644 Binary files a/src/skins/vector/img/cancel.png and b/src/skins/vector/img/cancel.png differ diff --git a/src/skins/vector/img/cancel.svg b/src/skins/vector/img/cancel.svg new file mode 100644 index 00000000..e3206002 --- /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/chevron-left.png b/src/skins/vector/img/chevron-left.png index 12abcc26..efb0065d 100644 Binary files a/src/skins/vector/img/chevron-left.png and b/src/skins/vector/img/chevron-left.png differ diff --git a/src/skins/vector/img/chevron-right.png b/src/skins/vector/img/chevron-right.png index 1fe5d347..18a4684e 100644 Binary files a/src/skins/vector/img/chevron-right.png and b/src/skins/vector/img/chevron-right.png differ diff --git a/src/skins/vector/img/chevron.png b/src/skins/vector/img/chevron.png index 3df8655b..81236f91 100644 Binary files a/src/skins/vector/img/chevron.png and b/src/skins/vector/img/chevron.png differ diff --git a/src/skins/vector/img/create-big.png b/src/skins/vector/img/create-big.png index 247b0030..b7307a11 100644 Binary files a/src/skins/vector/img/create-big.png and b/src/skins/vector/img/create-big.png differ diff --git a/src/skins/vector/img/create-big.svg b/src/skins/vector/img/create-big.svg new file mode 100644 index 00000000..2450542b --- /dev/null +++ b/src/skins/vector/img/create-big.svg @@ -0,0 +1,26 @@ + + + + icons_create_room + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/directory-big.png b/src/skins/vector/img/directory-big.png index bbcb16a4..03cab69c 100644 Binary files a/src/skins/vector/img/directory-big.png and b/src/skins/vector/img/directory-big.png differ diff --git a/src/skins/vector/img/directory-big.svg b/src/skins/vector/img/directory-big.svg new file mode 100644 index 00000000..5631a2ae --- /dev/null +++ b/src/skins/vector/img/directory-big.svg @@ -0,0 +1,22 @@ + + + + icons_directory + Created with sketchtool. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/download.svg b/src/skins/vector/img/download.svg new file mode 100644 index 00000000..d0ea090d --- /dev/null +++ b/src/skins/vector/img/download.svg @@ -0,0 +1,18 @@ + + + + Fill 75 + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/edit.png b/src/skins/vector/img/edit.png index 2686885f..6f373d3f 100644 Binary files a/src/skins/vector/img/edit.png and b/src/skins/vector/img/edit.png differ diff --git a/src/skins/vector/img/ellipsis.svg b/src/skins/vector/img/ellipsis.svg new file mode 100644 index 00000000..d60c8440 --- /dev/null +++ b/src/skins/vector/img/ellipsis.svg @@ -0,0 +1,25 @@ + + + + icons_archive + Created with Sketch. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/eol.svg b/src/skins/vector/img/eol.svg new file mode 100644 index 00000000..02d1946c --- /dev/null +++ b/src/skins/vector/img/eol.svg @@ -0,0 +1,16 @@ + + + + icon_eol + Created with sketchtool. + + + + + + + + + + + diff --git a/src/skins/vector/img/fileicon.png b/src/skins/vector/img/fileicon.png index be277a1c..af018efa 100644 Binary files a/src/skins/vector/img/fileicon.png and b/src/skins/vector/img/fileicon.png differ diff --git a/src/skins/vector/img/files.png b/src/skins/vector/img/files.png new file mode 100644 index 00000000..83932267 Binary files /dev/null and b/src/skins/vector/img/files.png differ diff --git a/src/skins/vector/img/files.svg b/src/skins/vector/img/files.svg new file mode 100644 index 00000000..20aba851 --- /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 00000000..e333abb6 --- /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 00000000..be038d2b --- /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 00000000..1acbe593 --- /dev/null +++ b/src/skins/vector/img/leave.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/skins/vector/img/list-close.png b/src/skins/vector/img/list-close.png new file mode 100644 index 00000000..82b322f9 Binary files /dev/null and b/src/skins/vector/img/list-close.png differ diff --git a/src/skins/vector/img/list-close.svg b/src/skins/vector/img/list-close.svg new file mode 100644 index 00000000..eb60864e --- /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.png b/src/skins/vector/img/list-open.png new file mode 100644 index 00000000..f8c80631 Binary files /dev/null and b/src/skins/vector/img/list-open.png differ diff --git a/src/skins/vector/img/list-open.svg b/src/skins/vector/img/list-open.svg new file mode 100644 index 00000000..a682ec90 --- /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/member_chevron.png b/src/skins/vector/img/member_chevron.png new file mode 100644 index 00000000..cbbd289d Binary files /dev/null and b/src/skins/vector/img/member_chevron.png differ diff --git a/src/skins/vector/img/members.png b/src/skins/vector/img/members.png index 6526f236..b5e58757 100644 Binary files a/src/skins/vector/img/members.png and b/src/skins/vector/img/members.png differ diff --git a/src/skins/vector/img/members.svg b/src/skins/vector/img/members.svg new file mode 100644 index 00000000..0f115966 --- /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/mod.svg b/src/skins/vector/img/mod.svg new file mode 100644 index 00000000..847baf98 --- /dev/null +++ b/src/skins/vector/img/mod.svg @@ -0,0 +1,16 @@ + + + + icons_admin + Created with sketchtool. + + + + + + + + + + + diff --git a/src/skins/vector/img/newmessages.png b/src/skins/vector/img/newmessages.png index e6dbeb17..a22156ab 100644 Binary files a/src/skins/vector/img/newmessages.png and b/src/skins/vector/img/newmessages.png differ diff --git a/src/skins/vector/img/newmessages.svg b/src/skins/vector/img/newmessages.svg new file mode 100644 index 00000000..a2ffca90 --- /dev/null +++ b/src/skins/vector/img/newmessages.svg @@ -0,0 +1,15 @@ + + + + icon_newmessages + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/plus.svg b/src/skins/vector/img/plus.svg new file mode 100644 index 00000000..e1d59ec6 --- /dev/null +++ b/src/skins/vector/img/plus.svg @@ -0,0 +1,13 @@ + + + + Line + Line + Created with Sketch. + + + + + + + + diff --git a/src/skins/vector/img/scrolldown.svg b/src/skins/vector/img/scrolldown.svg new file mode 100644 index 00000000..d6599c5f --- /dev/null +++ b/src/skins/vector/img/scrolldown.svg @@ -0,0 +1,15 @@ + + + + icon_newmessages + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/scrollup.svg b/src/skins/vector/img/scrollup.svg new file mode 100644 index 00000000..1692f2a6 --- /dev/null +++ b/src/skins/vector/img/scrollup.svg @@ -0,0 +1,91 @@ + + + + + + image/svg+xml + + + + + + + icon_newmessages + Created with Sketch. + + + + + + + + + + diff --git a/src/skins/vector/img/search-button.svg b/src/skins/vector/img/search-button.svg new file mode 100644 index 00000000..f4808842 --- /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.png b/src/skins/vector/img/search.png index d2c99855..2f98d290 100644 Binary files a/src/skins/vector/img/search.png and b/src/skins/vector/img/search.png differ diff --git a/src/skins/vector/img/search.svg b/src/skins/vector/img/search.svg new file mode 100644 index 00000000..bd4cd920 --- /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/selected.png b/src/skins/vector/img/selected.png new file mode 100644 index 00000000..8931cba7 Binary files /dev/null and b/src/skins/vector/img/selected.png differ diff --git a/src/skins/vector/img/settings-big.png b/src/skins/vector/img/settings-big.png index 3be13bc7..cb2e0a62 100644 Binary files a/src/skins/vector/img/settings-big.png and b/src/skins/vector/img/settings-big.png differ diff --git a/src/skins/vector/img/settings-big.svg b/src/skins/vector/img/settings-big.svg new file mode 100644 index 00000000..c9587d58 --- /dev/null +++ b/src/skins/vector/img/settings-big.svg @@ -0,0 +1,18 @@ + + + + icons_settings + Created with sketchtool. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/skins/vector/img/settings.png b/src/skins/vector/img/settings.png index 445a3909..264b3c9b 100644 Binary files a/src/skins/vector/img/settings.png and b/src/skins/vector/img/settings.png differ diff --git a/src/skins/vector/img/settings.svg b/src/skins/vector/img/settings.svg new file mode 100644 index 00000000..4190c7b8 --- /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 00000000..9b8de53d --- /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/tick.svg b/src/skins/vector/img/tick.svg new file mode 100644 index 00000000..6177f15f --- /dev/null +++ b/src/skins/vector/img/tick.svg @@ -0,0 +1,12 @@ + + + + icon_tick + Created with sketchtool. + + + + + + + diff --git a/src/skins/vector/img/trans.png b/src/skins/vector/img/trans.png new file mode 100644 index 00000000..8ba2310a Binary files /dev/null and b/src/skins/vector/img/trans.png differ diff --git a/src/skins/vector/img/upload-big.png b/src/skins/vector/img/upload-big.png index 7754f587..c11c0c45 100644 Binary files a/src/skins/vector/img/upload-big.png and b/src/skins/vector/img/upload-big.png differ diff --git a/src/skins/vector/img/upload-big.svg b/src/skins/vector/img/upload-big.svg new file mode 100644 index 00000000..6099c2e9 --- /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.png b/src/skins/vector/img/upload.png index 428501f0..7457bcd0 100644 Binary files a/src/skins/vector/img/upload.png and b/src/skins/vector/img/upload.png differ diff --git a/src/skins/vector/img/upload.svg b/src/skins/vector/img/upload.svg new file mode 100644 index 00000000..039014a2 --- /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 00000000..6de60ba3 --- /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 00000000..a6c6c3b6 --- /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 00000000..33664107 --- /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 00000000..0d7e6f42 --- /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 00000000..5ba765b0 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 00000000..ff87270b --- /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 00000000..5f7cbe71 --- /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 00000000..a16d1001 Binary files /dev/null and b/src/skins/vector/img/voip-mute.png differ diff --git a/src/skins/vector/img/warning.png b/src/skins/vector/img/warning.png new file mode 100644 index 00000000..c5553530 Binary files /dev/null and b/src/skins/vector/img/warning.png differ diff --git a/src/skins/vector/img/warning.svg b/src/skins/vector/img/warning.svg new file mode 100644 index 00000000..b9a96a88 --- /dev/null +++ b/src/skins/vector/img/warning.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/skins/vector/img/warning2.png b/src/skins/vector/img/warning2.png new file mode 100644 index 00000000..db0fd4a8 Binary files /dev/null and b/src/skins/vector/img/warning2.png differ diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js deleted file mode 100644 index 8dba10cf..00000000 --- a/src/skins/vector/skindex.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. -*/ - -/* - * 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.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.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.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.MatrixToolbar'] = require('./views/molecules/MatrixToolbar'); -skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo'); -skin['molecules.MemberTile'] = require('./views/molecules/MemberTile'); -skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile'); -skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer'); -skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu'); -skin['molecules.MessageTile'] = require('./views/molecules/MessageTile'); -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.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.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.RoomView'] = require('./views/organisms/RoomView'); -skin['organisms.UserSettings'] = require('./views/organisms/UserSettings'); -skin['organisms.ViewSource'] = require('./views/organisms/ViewSource'); -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 287ff9e2..00000000 --- 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 1848b029..00000000 --- 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/ImageView.js b/src/skins/vector/views/atoms/ImageView.js deleted file mode 100644 index 676348c0..00000000 --- a/src/skins/vector/views/atoms/ImageView.js +++ /dev/null @@ -1,73 +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'); - -module.exports = React.createClass({ - displayName: 'ImageView', - - // XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely... - componentDidMount: function() { - document.addEventListener("keydown", this.onKeyDown); - }, - - componentWillUnmount: function() { - document.removeEventListener("keydown", this.onKeyDown); - }, - - onKeyDown: function(ev) { - if (ev.keyCode == 27) { // escape - ev.stopPropagation(); - ev.preventDefault(); - this.props.onFinished(); - } - }, - - render: function() { - - // XXX: can't we just do max-width: 80%, max-height: 80% on the CSS? - - var width = this.props.width || 500; - var height = this.props.height || 500; - - var maxWidth = document.documentElement.clientWidth * 0.8; - var maxHeight = document.documentElement.clientHeight * 0.8; - - var widthFrac = width / maxWidth; - var heightFrac = height / maxHeight; - - var displayWidth; - var displayHeight; - if (widthFrac > heightFrac) { - displayWidth = Math.min(width, maxWidth); - displayHeight = (displayWidth / width) * height; - } else { - displayHeight = Math.min(height, maxHeight); - displayWidth = (displayHeight / height) * width; - } - - var style = { - width: displayWidth, - height: displayHeight - }; - - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/LogoutButton.js b/src/skins/vector/views/atoms/LogoutButton.js deleted file mode 100644 index 619160f6..00000000 --- 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 69652e1a..00000000 --- a/src/skins/vector/views/atoms/MemberAvatar.js +++ /dev/null @@ -1,49 +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() { - return ( - - ); - } -}); diff --git a/src/skins/vector/views/atoms/MessageTimestamp.js b/src/skins/vector/views/atoms/MessageTimestamp.js deleted file mode 100644 index 98cfe4a1..00000000 --- a/src/skins/vector/views/atoms/MessageTimestamp.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 React = require('react'); - -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 = React.createClass({ - displayName: 'MessageTimestamp', - - 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()); - } - }, - - render: function() { - var date = new Date(this.props.ts); - return ( - - { this.formatDate(date) } - - ); - }, -}); - diff --git a/src/skins/vector/views/atoms/RoomAvatar.js b/src/skins/vector/views/atoms/RoomAvatar.js deleted file mode 100644 index 3b5d4634..00000000 --- a/src/skins/vector/views/atoms/RoomAvatar.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 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 = [ '80cef4', '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 = { - maxWidth: this.props.width, - maxHeight: this.props.height, - }; - 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 2fc9d057..00000000 --- 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 a098a7d7..00000000 --- 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 0a8cadc8..00000000 --- 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/atoms/voip/VideoFeed.js b/src/skins/vector/views/atoms/voip/VideoFeed.js deleted file mode 100644 index 9cf28d1b..00000000 --- a/src/skins/vector/views/atoms/voip/VideoFeed.js +++ /dev/null @@ -1,31 +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'); - -module.exports = React.createClass({ - displayName: 'VideoFeed', - - 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 42c2d1fd..00000000 --- a/src/skins/vector/views/molecules/ChangeAvatar.js +++ /dev/null @@ -1,78 +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') - -var Loader = require("react-loader"); - - -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: - return ( - - ); - } - } -}); diff --git a/src/skins/vector/views/molecules/ChangeDisplayName.js b/src/skins/vector/views/molecules/ChangeDisplayName.js deleted file mode 100644 index 1a094ec2..00000000 --- a/src/skins/vector/views/molecules/ChangeDisplayName.js +++ /dev/null @@ -1,56 +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"); -var Loader = require("react-loader"); - - -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) { - 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 004fed39..00000000 --- a/src/skins/vector/views/molecules/ChangePassword.js +++ /dev/null @@ -1,85 +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') -var Loader = require("react-loader"); - - -module.exports = React.createClass({ - displayName: 'ChangePassword', - mixins: [ChangePasswordController], - - onClickChange: function() { - var old_password = this.refs.old_input.getDOMNode().value; - var new_password = this.refs.new_input.getDOMNode().value; - var confirm_password = this.refs.confirm_input.getDOMNode().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: - 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 389b8b40..00000000 --- a/src/skins/vector/views/molecules/EventTile.js +++ /dev/null @@ -1,131 +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 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.topic' : 'molecules.EventAsTextTile', -}; - -module.exports = React.createClass({ - displayName: 'EventTile', - mixins: [EventTileController], - - statics: { - supportsEventType: function(et) { - return eventTileTypes[et] !== undefined; - } - }, - - 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) { - return null; - } - - 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, - 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 f7e8991f..00000000 --- a/src/skins/vector/views/molecules/MFileTile.js +++ /dev/null @@ -1,44 +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(); - - return ( - - - - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MImageTile.js b/src/skins/vector/views/molecules/MImageTile.js deleted file mode 100644 index ed61a390..00000000 --- a/src/skins/vector/views/molecules/MImageTile.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 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 - }); - } - }, - - 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, 320, 240); - - var imgStyle = {}; - if (thumbHeight) imgStyle['height'] = thumbHeight; - - return ( - - - {content.body} - - - - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MNoticeTile.js b/src/skins/vector/views/molecules/MNoticeTile.js deleted file mode 100644 index aa886127..00000000 --- a/src/skins/vector/views/molecules/MNoticeTile.js +++ /dev/null @@ -1,36 +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 MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile') - -module.exports = React.createClass({ - displayName: 'MNoticeTile', - mixins: [MNoticeTileController], - - render: function() { - var content = this.props.mxEvent.getContent(); - return ( - - {content.body} - - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MRoomMemberTile.js b/src/skins/vector/views/molecules/MRoomMemberTile.js deleted file mode 100644 index 0048306d..00000000 --- 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 50555f94..00000000 --- a/src/skins/vector/views/molecules/MTextTile.js +++ /dev/null @@ -1,36 +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 MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile') - -module.exports = React.createClass({ - displayName: 'MTextTile', - mixins: [MTextTileController], - - render: function() { - var content = this.props.mxEvent.getContent(); - return ( - - {content.body} - - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MemberInfo.js b/src/skins/vector/views/molecules/MemberInfo.js deleted file mode 100644 index a2a3874a..00000000 --- a/src/skins/vector/views/molecules/MemberInfo.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 Loader = require("../atoms/Spinner"); - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo') - -module.exports = React.createClass({ - displayName: 'MemberInfo', - mixins: [MemberInfoController], - - 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) { - 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} -
    - } - - return ( -
    - {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 4c34fcb7..00000000 --- a/src/skins/vector/views/molecules/MemberTile.js +++ /dev/null @@ -1,187 +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 ContextualMenu = require('../../../../ContextualMenu'); -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) { - var self = this; - self.setState({ 'menu': true }); - var MemberInfo = sdk.getComponent('molecules.MemberInfo'); - ContextualMenu.createMenu(MemberInfo, { - member: self.props.member, - right: window.innerWidth - e.pageX, - top: e.pageY, - onFinished: function() { - self.setState({ 'menu': false }); - } - }); - }, - - 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 || this.state.menu) { - 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 || this.state.menu) { - 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 = -
    - { leave } -
    { this.props.member.userId }
    - { 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 c94cade5..00000000 --- a/src/skins/vector/views/molecules/MessageComposer.js +++ /dev/null @@ -1,67 +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') - -module.exports = React.createClass({ - displayName: 'MessageComposer', - mixins: [MessageComposerController], - - onUploadClick: function(ev) { - this.refs.uploadInput.getDOMNode().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.getDOMNode().value = null; - }, - - 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 = -
    - -
    - if (topic) topic_el =
    { topic.getContent().topic }
    ; - settings_button = ( -
    - -
    - ); - } - - var roomAvatar = null; - if (this.props.room) { - roomAvatar = ( - - ); - } - - if (activeCall && activeCall.type == "video") { - zoom_button = ( -
    - Fullscreen -
    - ); - } - - header = -
    -
    -
    - { roomAvatar } -
    -
    - { name } - { topic_el } -
    -
    - {call_buttons} - {cancel_button} - {save_button} -
    - { settings_button } - { zoom_button } -
    - Search -
    -
    - Video call -
    -
    - VoIP call -
    -
    -
    - } - - 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 c5e08ff9..00000000 --- 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.getDOMNode().value; - }, - - getJoinRules: function() { - return this.refs.is_private.getDOMNode().checked ? "invite" : "public"; - }, - - getHistoryVisibility: function() { - return this.refs.share_history.getDOMNode().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.getDOMNode().value), - kick: parseInt(this.refs.kick.getDOMNode().value), - redact: parseInt(this.refs.redact.getDOMNode().value), - invite: parseInt(this.refs.invite.getDOMNode().value), - events_default: parseInt(this.refs.events_default.getDOMNode().value), - state_default: parseInt(this.refs.state_default.getDOMNode().value), - users_default: parseInt(this.refs.users_default.getDOMNode().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 ( -
    -