diff --git a/.gitignore b/.gitignore index 13466ce843..2bc43f864a 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 0000000000..29e620a5ba --- /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 8617b58965..c08ad5e64b 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) <floviolleau at gmail dot com> + Improve README.md for a better understanding of installation instructions diff --git a/README.md b/README.md index 7bf2bea30b..16d4b056e0 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 0000000000..fd81929a07 --- /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 0000000000..a421c4d278 --- /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 cab45e836d..81975f97af 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 afc5e9dd6d..0000000000 --- a/src/Avatar.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -module.exports = { - avatarUrlForMember: function(member, width, height, resizeMethod) { - var url = member.getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - width, - height, - resizeMethod - ); - if (!url) { - // member can be null here currently since on invites, the JS SDK - // does not have enough info to build a RoomMember object for - // the inviter. - url = this.defaultAvatarUrlForString(member ? member.userId : ''); - } - return url; - }, - - defaultAvatarUrlForString: function(s) { - var total = 0; - for (var i = 0; i < s.length; ++i) { - total += s.charCodeAt(i); - } - switch (total % 3) { - case 0: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9QjNbxSKP4eagAFnTseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAABSwCRWJw31gAAAAASUVORK5CYII="; - case 1: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9chOaxgCP4eagAFk9seHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAtKMCks/JG8MAAAAASUVORK5CYII="; - case 2: - return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9YzNayQCP4eagADldseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAyiACeHwPiu4AAAAASUVORK5CYII="; - } - } -} - diff --git a/src/ContextualMenu.js b/src/ContextualMenu.js deleted file mode 100644 index 7865e45a75..0000000000 --- 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 = <img className="mx_ContextualMenu_chevron_left" src="img/chevron-left.png" width="9" height="16" /> - position.left = props.left + 8; - } else { - chevron = <img className="mx_ContextualMenu_chevron_right" src="img/chevron-right.png" width="9" height="16" /> - 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 = ( - <div className={className}> - <div className="mx_ContextualMenu" style={position}> - {chevron} - <Element {...props} onFinished={closeMenu}/> - </div> - <div className="mx_ContextualMenu_background" onClick={closeMenu}></div> - </div> - ); - - 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 637e34f943..aa88e77fac 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 0000000000..b25b5ef94c --- /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 2020d29d84..a4d89fcfed 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 <div className="mx_RoomTile_name">{name}</div> } else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); + var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); return <RoomTooltip name={name}/>; } }, render: function() { - var BottomLeftMenuTile = sdk.getComponent('molecules.BottomLeftMenuTile'); + var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile'); return ( <div className="mx_BottomLeftMenu"> <div className="mx_BottomLeftMenu_options"> - <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/create-big.png" label="Create new room" onClick={ this.onCreateRoomClick }/> - <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/directory-big.png" label="Directory" onClick={ this.onRoomDirectoryClick }/> - <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/settings-big.png" label="Settings" onClick={ this.onSettingsClick }/> + <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/create-big.svg" label="Start chat" onClick={ this.onCreateRoomClick }/> + <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/directory-big.svg" label="Directory" onClick={ this.onRoomDirectoryClick }/> + <BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/settings-big.svg" label="Settings" onClick={ this.onSettingsClick }/> </div> </div> ); diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js new file mode 100644 index 0000000000..6f2f93e6a0 --- /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 ( + <div className="mx_CompatibilityPage"> + <div className="mx_CompatibilityPage_box"> + <p>Sorry, your browser is <b>not</b> able to run Vector.</p> + <p> + Buttons and images may appear out of place, communication may + not be possible and all manner of chaos may be unleashed. + </p> + <p> + Please install <a href={"https://www.google.com/chrome"}>Chrome</a> for + the best experience. + </p> + <p> + Though if you like taking risks with your life, you can still try it + out by clicking that you understand the risks involved. + </p> + <button onClick={this.onAccept}> + I understand the risks and wish to continue + </button> + </div> + </div> + ); + } +}); diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js new file mode 100644 index 0000000000..c80c481868 --- /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 = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/> + } + + var callPreview; + if (this.state.showCallElement && !this.props.collapsed) { + var CallView = sdk.getComponent('voip.CallView'); + callPreview = ( + <CallView + className="mx_LeftPanel_callView" onClick={this.onCallViewClick} + ConferenceHandler={VectorConferenceHandler} /> + ); + } + + return ( + <aside className={classes}> + { collapseButton } + { callPreview } + <RoomList + selectedRoom={this.props.selectedRoom} + collapsed={this.props.collapsed} + ConferenceHandler={VectorConferenceHandler} /> + <BottomLeftMenu collapsed={this.props.collapsed}/> + </aside> + ); + } +}); + +module.exports = DragDropContext(HTML5Backend)(LeftPanel); diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js new file mode 100644 index 0000000000..dc4f606c31 --- /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 = <div className="mx_RightPanel_headerButton_highlight"></div>; + } + else if (this.state.phase == this.Phase.FileList) { + filesHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>; + } + } + + 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 = <div className="mx_RightPanel_headerButton_badge">{ room.getJoinedMembers().length }</div>; + } + } + + if (this.props.roomId) { + buttonGroup = + <div className="mx_RightPanel_headerButtonGroup"> + <div className="mx_RightPanel_headerButton" title="Members" onClick={ this.onMemberListButtonClick }> + <TintableSvg src="img/members.svg" width="17" height="22"/> + { membersBadge } + { membersHighlight } + </div> + <div className="mx_RightPanel_headerButton mx_RightPanel_filebutton" title="Files"> + <TintableSvg src="img/files.svg" width="17" height="22"/> + { filesHighlight } + </div> + </div>; + + if (!this.props.collapsed) { + if(this.state.phase == this.Phase.MemberList) { + panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} /> + } + else if(this.state.phase == this.Phase.MemberInfo) { + var MemberInfo = sdk.getComponent('rooms.MemberInfo'); + panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId} /> + } + } + + } + + var classes = "mx_RightPanel"; + if (this.props.collapsed) { + classes += " collapsed"; + } + + return ( + <aside className={classes}> + <div className="mx_RightPanel_header"> + { buttonGroup } + </div> + { panel } + </aside> + ); + } +}); + diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js new file mode 100644 index 0000000000..6cc90f7968 --- /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 = ( + <div className="mx_RoomDirectory_perm">World readable</div> + ); + } + if (rooms[i].guest_can_join) { + guestJoin = ( + <div className="mx_RoomDirectory_perm">Guests can join</div> + ); + } + + perms = null; + if (guestRead || guestJoin) { + perms = <div className="mx_RoomDirectory_perms">{guestRead} {guestJoin}</div>; + } + + var topic = rooms[i].topic || ''; + topic = linkifyString(sanitizeHtml(topic)); + + rows.unshift( + <tr key={ rooms[i].room_id } onClick={self.showRoom.bind(null, rooms[i].room_id)}> + <td className="mx_RoomDirectory_roomAvatar"> + <BaseAvatar width={24} height={24} resizeMethod='crop' + name={ name } idName={ name } + url={ ContentRepo.getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + rooms[i].avatar_url, 24, 24, "crop") } /> + </td> + <td className="mx_RoomDirectory_roomDescription"> + <div className="mx_RoomDirectory_name">{ name }</div> + { perms } + <div className="mx_RoomDirectory_topic" + onClick={ function(e) { e.stopPropagation() } } + dangerouslySetInnerHTML={{ __html: topic }}/> + <div className="mx_RoomDirectory_alias">{ rooms[i].aliases[0] }</div> + </td> + <td className="mx_RoomDirectory_roomMemberCount"> + { rooms[i].num_joined_members } + </td> + </tr> + ); + } + 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 ( + <div className="mx_RoomDirectory"> + <Loader /> + </div> + ); + } + + var RoomHeader = sdk.getComponent('rooms.RoomHeader'); + return ( + <div className="mx_RoomDirectory"> + <RoomHeader simpleHeader="Directory" /> + <div className="mx_RoomDirectory_list"> + <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/> + <GeminiScrollbar className="mx_RoomDirectory_tableWrapper"> + <table ref="directory_table" className="mx_RoomDirectory_table"> + <tbody> + { this.getRows(this.state.roomAlias) } + </tbody> + </table> + </GeminiScrollbar> + </div> + </div> + ); + } +}); + diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js new file mode 100644 index 0000000000..2eeec2a688 --- /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 ( + <RoomTile + room={ room } + roomSubList={ self } + key={ room.roomId } + collapsed={ self.props.collapsed || false} + selected={ selected } + unread={ Unread.doesRoomHaveUnreadMessages(room) } + highlight={ room.getUnreadNotificationCount('highlight') > 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 ( + <h2 onClick={ this.onClick } className="mx_RoomSubList_label"> + { this.props.collapsed ? '' : this.props.label } + <TintableSvg className="mx_RoomSubList_chevron" + src={ this.state.hidden ? "img/list-close.svg" : "img/list-open.svg" } + width="10" height="10" /> + </h2> + ); + }, + + _createOverflowTile: function(overflowCount, totalCount) { + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + // XXX: this is duplicated from RoomTile - factor it out + return ( + <div className="mx_RoomTile mx_RoomTile_ellipsis" onClick={this._showFullMemberList}> + <div className="mx_RoomTile_avatar"> + <BaseAvatar url="img/ellipsis.svg" name="..." width={24} height={24} /> + </div> + <div className="mx_RoomTile_name">and { overflowCount } others...</div> + </div> + ); + }, + + _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 = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>; + } + + if (this.state.sortedList.length > 0 || this.props.editable) { + var subList; + var classes = "mx_RoomSubList"; + + if (!this.state.hidden) { + subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt} + createOverflowElement={this._createOverflowTile} > + { target } + { this.makeRoomTiles() } + </TruncatedList>; + } + else { + subList = <TruncatedList className={ classes }> + </TruncatedList>; + } + + return connectDropTarget( + <div> + { this._getHeaderJsx() } + { subList } + </div> + ); + } + else { + var Loader = sdk.getComponent("elements.Spinner"); + return ( + <div className="mx_RoomSubList"> + { this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined } + { (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined } + </div> + ); + } + } +}); + +// 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 0000000000..179f755328 --- /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 ( + <div className="mx_ViewSource"> + <pre> + {JSON.stringify(this.props.mxEvent.event, null, 2)} + </pre> + </div> + ); + } +}); + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js new file mode 100644 index 0000000000..ef00b387e9 --- /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 ( + <div className="mx_ImageView"> + <div className="mx_ImageView_lhs"> + </div> + <div className="mx_ImageView_content"> + <img src={this.props.src} style={style}/> + <div className="mx_ImageView_labelWrapper"> + <div className="mx_ImageView_label"> + <img className="mx_ImageView_cancel" src="img/cancel-white.svg" width="18" height="18" alt="Close" onClick={ this.props.onFinished }/> + <div className="mx_ImageView_shim"> + </div> + <div className="mx_ImageView_name"> + { this.props.mxEvent.getContent().body } + </div> + <div className="mx_ImageView_metadata"> + Uploaded on { DateUtils.formatDate(new Date(this.props.mxEvent.getTs())) } by { this.props.mxEvent.getSender() } + </div> + <a className="mx_ImageView_link" href={ this.props.src } target="_blank"> + <div className="mx_ImageView_download"> + Download this file<br/> + <span className="mx_ImageView_size">{ size } { res }</span> + </div> + </a> + <div className="mx_ImageView_button"> + <a className="mx_ImageView_link" href={ this.props.src } target="_blank"> + View full screen + </a> + </div> + <div className="mx_ImageView_button" onClick={this.onRedactClick}> + Redact + </div> + <div className="mx_ImageView_shim"> + </div> + </div> + </div> + </div> + <div className="mx_ImageView_rhs"> + </div> + </div> + ); + } +}); 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 908f267857..2b620f12c5 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 ( - <div> + <div className="mx_Spinner"> <img src="img/spinner.gif" width={w} height={h} className={imgClass}/> </div> ); diff --git a/src/components/views/globals/GuestWarningBar.js b/src/components/views/globals/GuestWarningBar.js new file mode 100644 index 0000000000..88940f353b --- /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 ( + <div className="mx_GuestWarningBar"> + <img className="mx_GuestWarningBar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/> + <div> + You are using Vector as a guest. <a onClick={this.onRegisterClicked}>Register</a> or <a onClick={this.onLoginClicked}>log in</a> to access more rooms and features. + </div> + </div> + ); + } +}); + 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 4a299f1415..b176ff2ea2 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 ( <div className="mx_MatrixToolbar"> - You are not receiving desktop notifications. <EnableNotificationsButton /> - <div className="mx_MatrixToolbar_close"><img src="img/close-white.png" width="16" height="16" onClick={ this.hideToolbar } /></div> + <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/> + <div> + You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a> + </div> + <div className="mx_MatrixToolbar_close"><img src="img/cancel.svg" width="18" height="18" onClick={ this.hideToolbar } /></div> </div> ); } 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 b1e1507796..83dccf5d0e 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 ( - <div className="mx_RoomDropTarget"> - {this.props.text} + <div className="mx_MatrixToolbar"> + <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/> + <div> + A new version of Vector is available. Refresh your browser. + </div> </div> ); } }); + diff --git a/src/components/views/login/VectorCustomServerDialog.js b/src/components/views/login/VectorCustomServerDialog.js new file mode 100644 index 0000000000..b3e99a97d7 --- /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 ( + <div className="mx_ErrorDialog"> + <div className="mx_Dialog_title"> + Custom Server Options + </div> + <div className="mx_Dialog_content"> + <span> + You can use the custom server options to log into other Matrix + servers by specifying a different Home server URL. + <br/> + This allows you to use Vector with an existing Matrix account on + a different Home server. + <br/> + <br/> + 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. + </span> + </div> + <div className="mx_Dialog_buttons"> + <button onClick={this.props.onFinished} autoFocus={true}> + Dismiss + </button> + </div> + </div> + ); + } +}); 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 ec644a4ec4..a5c6bbf8dd 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 ( - <div className="mx_EventAsTextTile"> - {TextForEvent.textForEvent(this.props.mxEvent)} + <div className="mx_Login_links"> + <a href="https://medium.com/@Vector">blog</a> · + <a href="https://twitter.com/@VectorCo">twitter</a> · + <a href="https://github.com/vector-im/vector-web">github</a> · + <a href="https://matrix.org">powered by Matrix</a> </div> ); - }, + } }); - 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 a00cfc85cd..0a3a8c5107 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 ( - <div className="mx_ViewSource"> - <pre> - {JSON.stringify(this.props.mxEvent.event, null, 2)} - </pre> + <div className="mx_Login_logo"> + <img src="img/logo.png" width="249" height="78" alt="vector"/> </div> ); } }); - 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 061ce66daf..89cc44db89 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 e8cd322aa4..609b5f4fce 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 ( - <span className="mx_UnknownMessageTile"> - {content.body} + <span className="mx_MessageTimestamp"> + { DateUtils.formatDate(date) } </span> ); }, }); + 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 de2d93650b..9455cacb4b 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 ( - <span ref="content" className="mx_MEmoteTile mx_MessageTile_content"> - * {name} {content.body} + <span className="mx_SenderProfile"> + {name} { this.props.aux } </span> ); }, 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 2644769ccd..0db6bd92eb 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 = <div className="mx_RoomTile_name">{ this.props.label }</div>; } else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); + var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); label = <RoomTooltip bottom={ true } label={ this.props.label }/>; } return ( <div className="mx_RoomTile" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.props.onClick}> <div className="mx_RoomTile_avatar"> - <img src={ this.props.img } width="36" height="36"/> + <img src={ this.props.img } width="26" height="26"/> </div> { label } </div> 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 249d9a3437..a4631dfa6b 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 = ( <div className="mx_ContextualMenu_field" onClick={this.onResendClick}> Resend </div> ); } - else { + + if (!eventStatus) { // sent redactButton = ( <div className="mx_ContextualMenu_field" onClick={this.onRedactClick}> - Delete + Redact </div> ); } + + if (eventStatus === "queued" || eventStatus === "not_sent") { + cancelButton = ( + <div className="mx_ContextualMenu_field" onClick={this.onCancelSendClick}> + Cancel Sending + </div> + ); + } + viewSourceButton = ( <div className="mx_ContextualMenu_field" onClick={this.onViewSourceClick}> View Source @@ -98,6 +103,7 @@ module.exports = React.createClass({ <div> {resendButton} {redactButton} + {cancelButton} {viewSourceButton} </div> ); diff --git a/src/components/views/rooms/RoomDNDView.js b/src/components/views/rooms/RoomDNDView.js new file mode 100644 index 0000000000..e5c8fb334b --- /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 edef9edc68..789ba8fa85 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 ( - <button className="mx_EnableNotificationsButton" onClick={this.onClick}>Disable Notifications</button> + <div className="mx_RoomDropTarget mx_RoomDropTarget_placeholder"> + </div> ); - } else { + } + else { return ( - <button className="mx_EnableNotificationsButton" onClick={this.onClick}>Enable Notifications</button> + <div className="mx_RoomDropTarget"> + <div className="mx_RoomDropTarget_avatar"></div> + <div className="mx_RoomDropTarget_label"> + { this.props.label } + </div> + </div> ); } } 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 82e3e74468..e3cbc6ec0c 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 0000000000..955b4c51b6 --- /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 ( + <div className="mx_SearchBar"> + <input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/> + <div className={ searchButtonClasses } onClick={this.onSearch}><img src="img/search-button.svg" width="37" height="37" alt="Search"/></div> + <div className={ thisRoomClasses } onClick={this.onThisRoomClick}>This Room</div> + <div className={ allRoomsClasses } onClick={this.onAllRoomsClick}>All Rooms</div> + <img className="mx_SearchBar_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.props.onCancelClick} /> + </div> + ); + } +}); diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js new file mode 100644 index 0000000000..4c880760ba --- /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" : (<span>Messages containing <span className="mx_UserNotifSettings_keywords" onClick={ self.onKeywordsClicked }>keywords</span></span>), + "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 ( + <tr key = {className}> + <th> + {title} + </th> + + <th> + <input className= {className + "-" + PushRuleVectorState.ON} + type="radio" + checked={ pushRuleVectorState === PushRuleVectorState.ON } + onChange={ this.onNotifStateButtonClicked } /> + </th> + + <th> + <input className= {className + "-" + PushRuleVectorState.LOUD} + type="radio" + checked={ pushRuleVectorState === PushRuleVectorState.LOUD } + onChange={ this.onNotifStateButtonClicked } /> + </th> + + <th> + <input className= {className + "-" + PushRuleVectorState.OFF} + type="radio" + checked={ pushRuleVectorState === PushRuleVectorState.OFF } + onChange={ this.onNotifStateButtonClicked } /> + </th> + </tr> + ); + }, + + 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 ( + <div className="mx_UserSettings_notifTable"> + <Loader /> + </div> + ); + } + + if (this.state.masterPushRule) { + var masterPushRuleDiv = ( + <div className="mx_UserNotifSettings_tableRow"> + <div className="mx_UserNotifSettings_inputCell"> + <input id="enableNotifications" + ref="enableNotifications" + type="checkbox" + checked={ !this.state.masterPushRule.enabled } + onChange={ this.onEnableNotificationsChange } /> + </div> + <div className="mx_UserNotifSettings_labelCell"> + <label htmlFor="enableNotifications"> + Enable notifications + </label> + </div> + </div> + ); + } + + // When enabled, the master rule inhibits all existing rules + // So do not show all notification settings + if (this.state.masterPushRule.enabled) { + return ( + <div> + {masterPushRuleDiv} + + <div className="mx_UserSettings_notifTable"> + All notifications are currently disabled for all devices. + </div> + </div> + ); + } + + // Build external push rules + var externalRules = []; + for (var i in this.state.externalPushRules) { + var rule = this.state.externalPushRules[i]; + externalRules.push(<li>{ rule.description }</li>); + } + + // 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(<li>Notifications on the following keywords follow rules which can’t be displayed here: { externalKeyWords }</li>); + } + + var advancedSettings; + if (externalRules.length) { + advancedSettings = ( + <div> + <h3>Advanced notifications settings</h3> + There are advanced notifications which are not shown here.<br/> + You might have configured them in another client than Vector. You cannot tune them in Vector but they still apply. + <ul> + { externalRules } + </ul> + </div> + ); + } + + return ( + <div> + + {masterPushRuleDiv} + + <div className="mx_UserSettings_notifTable"> + + <div className="mx_UserNotifSettings_tableRow"> + <div className="mx_UserNotifSettings_inputCell"> + <input id="enableDesktopNotifications" + ref="enableDesktopNotifications" + type="checkbox" + checked={ UserSettingsStore.getEnableNotifications() } + onChange={ this.onEnableDesktopNotificationsChange } /> + </div> + <div className="mx_UserNotifSettings_labelCell"> + <label htmlFor="enableDesktopNotifications"> + Enable desktop notifications + </label> + </div> + </div> + + <h3>General use</h3> + + <div className="mx_UserNotifSettings_pushRulesTableWrapper"> + <table className="mx_UserNotifSettings_pushRulesTable"> + <thead> + <tr> + <th width="55%"></th> + <th width="15%">On</th> + <th width="15%">Loud</th> + <th width="15%">Off</th> + </tr> + </thead> + <tbody> + + { this.renderNotifRulesTableRows() } + + </tbody> + </table> + </div> + + { advancedSettings } + + </div> + + </div> + ); + } +}); diff --git a/src/controllers/molecules/voip/CallView.js b/src/controllers/molecules/voip/CallView.js deleted file mode 100644 index ab71214882..0000000000 --- a/src/controllers/molecules/voip/CallView.js +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; -var dis = require("matrix-react-sdk/lib/dispatcher"); -var CallHandler = require("matrix-react-sdk/lib/CallHandler"); -var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); - -var VectorConferenceHandler = require('../../../modules/VectorConferenceHandler'); - -/* - * State vars: - * this.state.call = MatrixCall|null - * - * Props: - * this.props.room = Room (JS SDK) - * - * Internal state: - * this._trackedRoom = (either from props.room or programatically set) - */ - -module.exports = { - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - this._trackedRoom = null; - if (this.props.room) { - this._trackedRoom = this.props.room; - this.showCall(this._trackedRoom.roomId); - } - else { - var call = CallHandler.getAnyActiveCall(); - if (call) { - console.log( - "Global CallView is now tracking active call in room %s", - call.roomId - ); - this._trackedRoom = MatrixClientPeg.get().getRoom(call.roomId); - this.showCall(call.roomId); - } - } - }, - - componentWillUnmount: function() { - dis.unregister(this.dispatcherRef); - }, - - onAction: function(payload) { - // don't filter out payloads for room IDs other than props.room because - // we may be interested in the conf 1:1 room - if (payload.action !== 'call_state' || !payload.room_id) { - return; - } - this.showCall(payload.room_id); - }, - - showCall: function(roomId) { - var call = ( - CallHandler.getCallForRoom(roomId) || - VectorConferenceHandler.getConferenceCallForRoom(roomId) - ); - if (call) { - call.setLocalVideoElement(this.getVideoView().getLocalVideoElement()); - call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement()); - // give a separate element for audio stream playback - both for voice calls - // and for the voice stream of screen captures - call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement()); - } - if (call && call.type === "video" && call.state !== 'ended') { - // if this call is a conf call, don't display local video as the - // conference will have us in it - this.getVideoView().getLocalVideoElement().style.display = ( - call.confUserId ? "none" : "initial" - ); - this.getVideoView().getRemoteVideoElement().style.display = "initial"; - } - else { - this.getVideoView().getLocalVideoElement().style.display = "none"; - this.getVideoView().getRemoteVideoElement().style.display = "none"; - dis.dispatch({action: 'video_fullscreen', fullscreen: false}); - } - } -}; - diff --git a/src/controllers/organisms/RoomList.js b/src/controllers/organisms/RoomList.js deleted file mode 100644 index 151a6ca278..0000000000 --- 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 ( - <RoomTile - room={room} - key={room.roomId} - collapsed={self.props.collapsed} - selected={selected} - unread={self.state.activityMap[room.roomId] === 1} - highlight={self.state.activityMap[room.roomId] === 2} - /> - ); - }); - } -}; diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js deleted file mode 100644 index 21027cbfa8..0000000000 --- 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 = <DateSeparator key={ts1} ts={ts1}/>; - 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 = <li key={ts1}><DateSeparator ts={ts1}/></li>; - continuation = false; - } - - ret.unshift( - <li key={mxEv.getId()}><EventTile mxEvent={mxEv} continuation={continuation} last={last}/></li> - ); - if (dateSeparator) { - ret.unshift(dateSeparator); - } - ++count; - } - return ret; - }, - - uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { - var old_name = this.state.room.name; - - var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); - if (old_topic) { - old_topic = old_topic.getContent().topic; - } else { - old_topic = ""; - } - - var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); - if (old_join_rule) { - old_join_rule = old_join_rule.getContent().join_rule; - } else { - old_join_rule = "invite"; - } - - var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); - if (old_history_visibility) { - old_history_visibility = old_history_visibility.getContent().history_visibility; - } else { - old_history_visibility = "shared"; - } - - var deferreds = []; - - if (old_name != new_name && new_name != undefined && new_name) { - deferreds.push( - MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) - ); - } - - if (old_topic != new_topic && new_topic != undefined) { - deferreds.push( - MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) - ); - } - - if (old_join_rule != new_join_rule && new_join_rule != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.join_rules", { - join_rule: new_join_rule, - }, "" - ) - ); - } - - if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.history_visibility", { - history_visibility: new_history_visibility, - }, "" - ) - ); - } - - if (new_power_levels) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.power_levels", new_power_levels, "" - ) - ); - } - - if (deferreds.length) { - var self = this; - q.all(deferreds).fail(function(err) { - var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to set state", - description: err.toString() - }); - }).finally(function() { - self.setState({ - uploadingRoomSettings: false, - }); - }); - } else { - this.setState({ - editingRoomSettings: false, - uploadingRoomSettings: false, - }); - } - } -}; diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js deleted file mode 100644 index 5f88d59059..0000000000 --- a/src/controllers/templates/Register.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var extend = require('matrix-react-sdk/lib/extend'); -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var BaseRegisterController = require('matrix-react-sdk/lib/controllers/templates/Register.js'); - -var RegisterController = {}; -extend(RegisterController, BaseRegisterController); - -RegisterController.onRegistered = function(user_id, access_token) { - MatrixClientPeg.replaceUsingAccessToken( - this.state.hs_url, this.state.is_url, user_id, access_token - ); - - this.setState({ - step: 'profile', - busy: true - }); - - var self = this; - var cli = MatrixClientPeg.get(); - cli.getProfileInfo(cli.credentials.userId).done(function(result) { - self.setState({ - avatarUrl: result.avatar_url, - busy: false - }); - }, - function(err) { - console.err(err); - self.setState({ - busy: false - }); - }); -}; - -RegisterController.onAccountReady = function() { - if (this.props.onLoggedIn) { - this.props.onLoggedIn(); - } -}; - -module.exports = RegisterController; diff --git a/src/skins/vector/header b/src/header similarity index 93% rename from src/skins/vector/header rename to src/header index fd88ee27f7..060709b82e 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 93012c0f11..53ab57fe05 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 fbc2db207e..f84a35b313 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 d6b1765c3f..d9b121fe5f 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 6fa0415af7..e8a5eb795b 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 0000000000..d36035e633 --- /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 2aab203a4c..74fa198d5a 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 0000000000..bff271a354 --- /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 0000000000..ea1710dac6 --- /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 93cb7433a4..5ee580046f 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 99c2824082..901a29599a 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 0000000000..66a796720d --- /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 8b8adc09c1..7b5e0c7461 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 db0572c841..75cd4170da 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 10e5b50b60..8af3701ef7 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/organisms/UserSettings.css b/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css similarity index 82% rename from src/skins/vector/css/organisms/UserSettings.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css index b69399b72b..a88c20863d 100644 --- a/src/skins/vector/css/organisms/UserSettings.css +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.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,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_UserSettings { - width: 720px; - margin-left: auto; - margin-right: auto; +.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 5b117e41b8..93a89ad1b7 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/molecules/MemberInfo.css b/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css similarity index 84% rename from src/skins/vector/css/molecules/MemberInfo.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css index 52c48a795f..be7565b3c5 100644 --- a/src/skins/vector/css/molecules/MemberInfo.css +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.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,3 +14,7 @@ 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 0000000000..be7b474de5 --- /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 0000000000..c4ed9acbb1 --- /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 0000000000..4d421007dc --- /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 0000000000..fb24cc3cec --- /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 af4934ee2a..6eca5415ba 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 0e9c3ca144..682c849cee 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 0000000000..664c31703b --- /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 c42d44995d..1a4ec869f2 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 aab0def49a..5a6ee551cf 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 0000000000..172da8eb51 --- /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 511fc94faf..f562c76ca6 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 0000000000..0d7639bfca --- /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 0000000000..e0d0965e34 --- /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 0000000000..35f68399da --- /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 01cdb45ae1..179fd29467 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 2c57a3273a..5cf62091b3 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 b47039c340..8f23ef6ba9 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 1cd2fa465f..0000000000 --- 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 faae142abf..0000000000 --- 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 f7adb6189f..0000000000 --- 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 3a66f0e906..0000000000 --- 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/organisms/ViewSource.css b/src/skins/vector/css/organisms/ViewSource.css deleted file mode 100644 index ae61ae5821..0000000000 --- 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 0000000000..f3f032c975 --- /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 0e7e077e4b..68f689b6af 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 30ef47264a..12e101a37a 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 21985a257d..a61e9e43aa 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 0000000000..d385397beb --- /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 6422df798e..c4017e455b 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 0000000000..79d0b1af4f --- /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 21cb781227..3831cc4c1c 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 0000000000..717d75afe4 --- /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 0000000000..b5e910210e --- /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 b3c6850949..e21189c59e 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/SenderProfile.css b/src/skins/vector/css/vector-web/views/messages/SenderProfile.css similarity index 78% rename from src/skins/vector/css/molecules/SenderProfile.css rename to src/skins/vector/css/vector-web/views/messages/SenderProfile.css index 45db913e68..060709b82e 100644 --- a/src/skins/vector/css/molecules/SenderProfile.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,8 +13,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - -.mx_SenderProfile_zalgo { - font-family: Helvetica, Arial, Sans-Serif; - display: table-row ! important; -} diff --git a/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css b/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css new file mode 100644 index 0000000000..7ad5e89334 --- /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 f45970febe..3aec0fa7f6 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 0000000000..37239a9558 --- /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 0000000000..f0446e43b9 --- /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 0000000000..cba0081a99 --- /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 0000000000..bffc5fcf61 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 0000000000..4001c52a35 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 0000000000..402dfd77bc 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 0000000000..51c75cadf1 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 0000000000..89ce23b4b4 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 0000000000..de1ea60d54 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 0000000000..7ea7459304 --- /dev/null +++ b/src/skins/vector/img/admin.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="16px" height="17px" viewBox="-1 -1 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4.4 (395) - http://www.bohemiancoding.com/sketch --> + <title>icons_owner</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_19-Room-contextual-menu-hover" sketch:type="MSArtboardGroup" transform="translate(-1000.000000, -128.000000)"> + <g id="people_open" sketch:type="MSLayerGroup" transform="translate(966.000000, 59.000000)"> + <g id="icons_owner" transform="translate(35.000000, 70.000000)" sketch:type="MSShapeGroup"> + <path d="M0.441894529,1.80537109 C2.59277353,3.03442388 4.25305977,2.17675781 5.9832796,0.805371094 C8.01666135,2.17675787 9.50756797,3.12670903 11.6293941,1.80537109 C11.6293941,7.01538067 11.9379879,12.2253912 5.9832796,12.2253906 C0.0285712975,12.2253901 0.441894531,7.01538067 0.441894529,1.80537109 Z" id="Path-2-Copy" stroke="#FFFFFF" fill="#F6A623"></path> + <polygon id="Star-1" fill="#FFFFFF" points="6 8.8 3.88397309 9.91246118 4.28809827 7.55623059 2.57619654 5.88753882 4.94198655 5.54376941 6 3.4 7.05801345 5.54376941 9.42380346 5.88753882 7.71190173 7.55623059 8.11602691 9.91246118 "></polygon> + </g> + </g> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/call.png b/src/skins/vector/img/call.png new file mode 100644 index 0000000000..a7805e0596 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 0000000000..f528f9a24e --- /dev/null +++ b/src/skins/vector/img/call.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="30px" height="22px" viewBox="-1 -1 30 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_video</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -723.000000)" stroke="#76CFA6"> + <g id="icons_video" sketch:type="MSLayerGroup" transform="translate(910.000000, 722.000000)"> + <g id="Rectangle-20-+-Path-16" transform="translate(0.000000, 0.464286)" sketch:type="MSShapeGroup"> + <rect id="Rectangle-20" fill="#FFFFFF" x="0" y="0.535714286" width="20" height="20" rx="4"></rect> + <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path> + </g> + </g> + </g> + </g> +</svg> \ 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 0000000000..6519496f78 --- /dev/null +++ b/src/skins/vector/img/camera.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="17px" height="15px" viewBox="-1 -1 16 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icon_camera</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="06a-Room-settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="06_4-Room-settings-admin" sketch:type="MSArtboardGroup" transform="translate(-248.000000, -71.000000)" fill="#454545"> + <path d="M255.5,76.25 C256.119795,76.25 256.649737,76.4700499 257.089844,76.9101562 C257.52995,77.3502626 257.75,77.8802052 257.75,78.5 C257.75,79.1197948 257.52995,79.6497374 257.089844,80.0898438 C256.649737,80.5299501 256.119795,80.75 255.5,80.75 C254.880205,80.75 254.350263,80.5299501 253.910156,80.0898438 C253.47005,79.6497374 253.25,79.1197948 253.25,78.5 C253.25,77.8802052 253.47005,77.3502626 253.910156,76.9101562 C254.350263,76.4700499 254.880205,76.25 255.5,76.25 L255.5,76.25 Z M261,73 C261.552086,73 262.023436,73.1953105 262.414062,73.5859375 C262.804689,73.9765645 263,74.4479139 263,75 L263,82 C263,82.5520861 262.804689,83.0234355 262.414062,83.4140625 C262.023436,83.8046895 261.552086,84 261,84 L250,84 C249.447914,84 248.976564,83.8046895 248.585938,83.4140625 C248.195311,83.0234355 248,82.5520861 248,82 L248,75 C248,74.4479139 248.195311,73.9765645 248.585938,73.5859375 C248.976564,73.1953105 249.447914,73 250,73 L251.75,73 L252.148438,71.9375 C252.247396,71.6822904 252.428384,71.4622405 252.691406,71.2773438 C252.954428,71.092447 253.223957,71 253.5,71 L257.5,71 C257.776043,71 258.045572,71.092447 258.308594,71.2773438 C258.571616,71.4622405 258.752604,71.6822904 258.851562,71.9375 L259.25,73 L261,73 Z M255.5,82 C256.463546,82 257.287757,81.6575555 257.972656,80.9726562 C258.657556,80.287757 259,79.4635465 259,78.5 C259,77.5364535 258.657556,76.712243 257.972656,76.0273438 C257.287757,75.3424445 256.463546,75 255.5,75 C254.536454,75 253.712243,75.3424445 253.027344,76.0273438 C252.342444,76.712243 252,77.5364535 252,78.5 C252,79.4635465 252.342444,80.287757 253.027344,80.9726562 C253.712243,81.6575555 254.536454,82 255.5,82 L255.5,82 Z" id="icon_camera" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/cancel-black.png b/src/skins/vector/img/cancel-black.png new file mode 100644 index 0000000000..87dcfd41a8 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 0000000000..a928c61b09 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 0000000000..e4c8cafc10 --- /dev/null +++ b/src/skins/vector/img/cancel-small.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="14px" height="14px" viewBox="-1 -1 13 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Line + Line</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="Line-+-Line" sketch:type="MSLayerGroup" transform="translate(2.000000, 2.000000)" stroke="#4A4A4A" stroke-width="2.82" stroke-linecap="square"> + <path d="M8,0 L0,8" id="Line" sketch:type="MSShapeGroup"></path> + <path d="M0,0 L8,8" id="Line" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> \ 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 0000000000..65e14c2fbc --- /dev/null +++ b/src/skins/vector/img/cancel-white.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.2 (15857) - http://www.bohemiancoding.com/sketch --> + <title>Slice 1</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <path d="M9.74464309,-3.02908503 L8.14106175,-3.02908503 L8.14106175,8.19448443 L-3.03028759,8.19448443 L-3.03028759,9.7978515 L8.14106175,9.7978515 L8.14106175,20.9685098 L9.74464309,20.9685098 L9.74464309,9.7978515 L20.9697124,9.7978515 L20.9697124,8.19448443 L9.74464309,8.19448443 L9.74464309,-3.02908503" id="Fill-108" opacity="0.9" fill="#ffffff" sketch:type="MSShapeGroup" transform="translate(8.969712, 8.969712) rotate(-315.000000) translate(-8.969712, -8.969712) "></path> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/cancel.png b/src/skins/vector/img/cancel.png index c963a4b7b3..2bda8ff5bf 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 0000000000..e32060025e --- /dev/null +++ b/src/skins/vector/img/cancel.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.2 (15857) - http://www.bohemiancoding.com/sketch --> + <title>Slice 1</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <path d="M9.74464309,-3.02908503 L8.14106175,-3.02908503 L8.14106175,8.19448443 L-3.03028759,8.19448443 L-3.03028759,9.7978515 L8.14106175,9.7978515 L8.14106175,20.9685098 L9.74464309,20.9685098 L9.74464309,9.7978515 L20.9697124,9.7978515 L20.9697124,8.19448443 L9.74464309,8.19448443 L9.74464309,-3.02908503" id="Fill-108" opacity="0.9" fill="#454545" sketch:type="MSShapeGroup" transform="translate(8.969712, 8.969712) rotate(-315.000000) translate(-8.969712, -8.969712) "></path> + </g> +</svg> \ 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 12abcc264b..efb0065de9 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 1fe5d347db..18a4684e47 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 3df8655bcf..81236f91bc 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 247b0030cd..b7307a11c7 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 0000000000..2450542b63 --- /dev/null +++ b/src/skins/vector/img/create-big.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="26px" height="26px" viewBox="-1 -1 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4 (381) - http://www.bohemiancoding.com/sketch --> + <title>icons_create_room</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="01-Sign-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="01_2-Sign-up-2" sketch:type="MSArtboardGroup" transform="translate(-14.000000, -653.000000)"> + <g id="Left-panel" sketch:type="MSLayerGroup" transform="translate(-4.000000, 0.000000)"> + <g id="right_bottom" transform="translate(0.000000, 626.000000)" sketch:type="MSShapeGroup"> + <g id="Group-Copy-29" transform="translate(18.000000, 27.000000)"> + <g id="Group-Copy-15"> + <g id="icons_create_room"> + <g id="Oval-1-Copy-7-+-Group-Copy-5-Copy-Copy-Copy-Copy-Copy-Copy-Copy-Copy-Copy-Copy-Copy" fill="#454545"> + <circle id="Oval-1-Copy-7" cx="12" cy="12" r="12"></circle> + </g> + <path d="M7,12 L17,12" id="Line" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></path> + <path d="M12,7 L12,17" id="Line" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></path> + </g> + </g> + </g> + </g> + </g> + </g> + </g> +</svg> \ 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 bbcb16a4b7..03cab69c4a 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 0000000000..5631a2ae3e --- /dev/null +++ b/src/skins/vector/img/directory-big.svg @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="26px" height="26px" viewBox="-1 -2 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4 (381) - http://www.bohemiancoding.com/sketch --> + <title>icons_directory</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="01-Sign-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="01_2-Sign-up-2" sketch:type="MSArtboardGroup" transform="translate(-14.000000, -689.000000)" fill="#454545"> + <g id="Left-panel" sketch:type="MSLayerGroup" transform="translate(-4.000000, 0.000000)"> + <g id="right_bottom" transform="translate(0.000000, 626.000000)" sketch:type="MSShapeGroup"> + <g id="Group-Copy-29" transform="translate(18.000000, 27.000000)"> + <g id="icons_directory" transform="translate(0.000000, 36.000000)"> + <path d="M8.74940618,2.46971496 C8.58491686,2.46971496 8.47565321,2.15795724 8.32719715,1.68349169 C8.11698337,1.01306413 7.80106888,0 6.69002375,0 L1.38182898,0 C0.63064133,0 0.0243467933,0.589073634 0.00118764846,1.35391924 L0,19.6264846 C0,20.3895487 0.620546318,21.0095012 1.38182898,21.0095012 L22.6187648,21.0095012 C23.3800475,21.0095012 24,20.3895487 24,19.6264846 L24,3.85213777 C24,3.08966746 23.3800475,2.46971496 22.6187648,2.46971496 L8.74940618,2.46971496 Z M23.1086698,19.5068597 C23.1086698,19.7757653 22.8889549,19.9946556 22.6187648,19.9946556 L1.38182898,19.9946556 C1.11163895,19.9946556 0.890736342,19.7757653 0.890736342,19.5068597 L0.89192399,6.49465558 L23.1086698,6.49465558 L23.1086698,19.5068597 L23.1086698,19.5068597 Z M23.1086698,5.60391924 L0.89192399,5.60391924 L0.89192399,1.36698337 C0.90023753,1.09976247 1.11579572,0.890736342 1.38182898,0.890736342 L6.69002375,0.890736342 C7.10273159,0.890736342 7.24643705,1.2131829 7.47624703,1.94893112 C7.66270784,2.54453682 7.91805226,3.36045131 8.74940618,3.36045131 L22.6187648,3.36045131 C22.8889549,3.36045131 23.1086698,3.5807601 23.1086698,3.85213777 L23.1086698,5.60391924 L23.1086698,5.60391924 Z" id="Fill-137"></path> + <path d="M23.1086698,19.5068597 C23.1086698,19.7757653 22.8889549,19.9946556 22.6187648,19.9946556 L1.38182898,19.9946556 C1.11163895,19.9946556 0.890736342,19.7757653 0.890736342,19.5068597 L0.89192399,6.49465558 L23.1086698,6.49465558 L23.1086698,19.5068597 L23.1086698,19.5068597 Z" id="Path-Copy-2" fill-opacity="0.1"></path> + <path d="M0.89192399,5.60391924 L0.89192399,1.36698337 C0.90023753,1.09976247 1.11579572,0.890736342 1.38182898,0.890736342 L6.69002375,0.890736342 C7.10273159,0.890736342 7.24643705,1.2131829 7.47624703,1.94893112 C7.66270784,2.54453682 7.91805226,3.36045131 8.74940618,3.36045131 L22.6187648,3.36045131 C22.8889549,3.36045131 23.1086698,3.5807601 23.1086698,3.85213777 L23.1086698,5.60391924 L0.89192399,5.60391924 Z" id="Path-Copy" fill-opacity="0.1"></path> + </g> + </g> + </g> + </g> + </g> + </g> +</svg> \ 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 0000000000..d0ea090d8a --- /dev/null +++ b/src/skins/vector/img/download.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="12px" height="14px" viewBox="-1 -1 12 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Fill 75</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_x-Chat-MAX" sketch:type="MSArtboardGroup" transform="translate(-280.000000, -546.000000)" fill="#76CFA6"> + <g id="Group" sketch:type="MSLayerGroup" transform="translate(243.000000, 58.000000)"> + <g id="Group-Copy-13-+-Matrix-HQ-Copy-17-+-Bitmap" transform="translate(1.000000, 181.000000)" sketch:type="MSShapeGroup"> + <g id="Group-Copy-13"> + <path d="M45.3400426,312.526774 C45.4870048,312.526774 45.633967,312.470898 45.7464849,312.35838 C45.9699899,312.134109 45.9699899,311.771296 45.7464849,311.547026 L41.3659396,307.168011 C41.1424345,306.943741 40.7788562,306.944506 40.5545857,307.167246 L36.1686823,311.540137 C35.9444119,311.764408 35.9436465,312.127986 36.1671515,312.352256 C36.3906565,312.577292 36.7542349,312.577292 36.9785053,312.353787 L40.3915495,308.950694 L40.3930804,318.425929 C40.3930804,318.742816 40.6510296,319 40.9671515,319 C41.2848042,319 41.5412226,318.742816 41.5412226,318.425929 L41.5396917,308.965237 L44.9343656,312.35838 C45.0461182,312.470898 45.1930804,312.526774 45.3400426,312.526774 L45.3400426,312.526774 Z" id="Fill-75" transform="translate(40.957057, 313.000000) rotate(-180.000000) translate(-40.957057, -313.000000) "></path> + </g> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/edit.png b/src/skins/vector/img/edit.png index 2686885f79..6f373d3f3d 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 0000000000..d60c844089 --- /dev/null +++ b/src/skins/vector/img/ellipsis.svg @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icons_archive</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="ellipsis" sketch:type="MSLayerGroup"> + <g id="03-Input" sketch:type="MSShapeGroup"> + <g id="03_3-Input-filled"> + <g id="right_default"> + <g id="right_middle"> + <g id="Left-panel"> + <g id="icons_archive"> + <rect id="Rectangle-1" fill="#ECECEC" x="0" y="0" width="24" height="24"></rect> + <path d="M7.338,13.154 C7.842,13.154 8.164,12.776 8.164,12.272 C8.164,11.754 7.842,11.39 7.338,11.39 C6.848,11.39 6.512,11.754 6.512,12.272 C6.498,12.776 6.848,13.154 7.338,13.154 L7.338,13.154 L7.338,13.154 Z M12,13.154 C12.504,13.154 12.84,12.776 12.84,12.272 C12.826,11.754 12.504,11.39 12.014,11.39 C11.524,11.39 11.174,11.754 11.174,12.272 C11.16,12.776 11.51,13.154 12,13.154 L12,13.154 L12,13.154 Z M16.662,13.154 C17.18,13.154 17.502,12.776 17.502,12.272 C17.502,11.754 17.166,11.39 16.676,11.39 C16.186,11.39 15.836,11.754 15.836,12.272 C15.836,12.776 16.172,13.154 16.662,13.154 L16.662,13.154 L16.662,13.154 Z" id="Matrix-HQ-Copy-15" fill="#454545"></path> + </g> + </g> + </g> + </g> + </g> + </g> + </g> + </g> +</svg> \ 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 0000000000..02d1946cf4 --- /dev/null +++ b/src/skins/vector/img/eol.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="22px" height="16px" viewBox="-1 -1 22 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4 (381) - http://www.bohemiancoding.com/sketch --> + <title>icon_eol</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="03-Input" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="03_2-Mentions-autocomplete" sketch:type="MSArtboardGroup" transform="translate(-772.000000, -670.000000)"> + <g id="icon_eol" sketch:type="MSLayerGroup" transform="translate(772.000000, 670.000000)"> + <path d="M0,7.5 L17,7.5" id="Path-118" stroke="#76CFA6" sketch:type="MSShapeGroup"></path> + <path d="M13,2 L18,7.38056399 L13,12.761128" id="Path-119" stroke="#76CFA6" sketch:type="MSShapeGroup"></path> + <path d="M19.5,0 L19.5,14" id="Path-120" stroke="#76CFA6" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/fileicon.png b/src/skins/vector/img/fileicon.png index be277a1c8a..af018efa6d 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 0000000000..83932267f8 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 0000000000..20aba851ea --- /dev/null +++ b/src/skins/vector/img/files.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="17px" height="22px" viewBox="0 0 17 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_browse_files</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-1025.000000, -33.000000)"> + <g id="icons_browse_files" sketch:type="MSLayerGroup" transform="translate(1025.000000, 32.000000)"> + <g id="Rectangle-5-+-Rectangle-6-Copy" transform="translate(0.000000, 1.000000)" sketch:type="MSShapeGroup"> + <path d="M0,4.00955791 C0,1.79514022 1.78163126,0 3.99825563,0 L9.59161955,0 C9.59161955,0 16.3225806,6.49234232 16.3225806,6.49234232 L16.3225806,18.0063928 C16.3225806,20.2120012 14.5290874,22 12.3296282,22 L3.99295243,22 C1.7877057,22 0,20.1996477 0,17.9904421 L0,4.00955791 Z" id="Rectangle-5" stroke="#76CFA6"></path> + <path d="M15.6804916,7.49527496 L11.5273266,7.49527496 C10.3308881,7.49527496 9.3609831,6.52527676 9.3609831,5.3289315 L9.3609831,1.88544393 L15.6804916,7.49527496 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path> + <path d="M16.3225806,7.09677419 L11.4129801,7.09677419 C10.2050375,7.09677419 9.22580645,6.11744908 9.22580645,4.90960051 L9.22580645,0" id="Rectangle-6" stroke="#76CFA6"></path> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/fullscreen.svg b/src/skins/vector/img/fullscreen.svg new file mode 100644 index 0000000000..e333abb6fb --- /dev/null +++ b/src/skins/vector/img/fullscreen.svg @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="29px" height="22px" viewBox="-1 -1 29 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Zoom</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-824.000000, -667.000000)"> + <g id="Zoom" sketch:type="MSLayerGroup" transform="translate(824.000000, 667.000000)"> + <rect id="Rectangle-20" stroke="#FFFFFF" sketch:type="MSShapeGroup" x="0" y="0" width="27" height="20" rx="4"></rect> + <rect id="Rectangle-20" stroke="#FFFFFF" sketch:type="MSShapeGroup" x="6" y="2" width="15" height="11" rx="4"></rect> + <g id="Line-+-Triangle-3" transform="translate(4.474874, 15.181981) rotate(-45.000000) translate(-4.474874, -15.181981) translate(0.974874, 13.181981)" sketch:type="MSShapeGroup"> + <path d="M6.53553391,2 L3,2" id="Line" stroke="#FFFFFF" stroke-linecap="square"></path> + <path d="M3.26960678,3.51960678 L0.269606781,2.01960678 L3.26960678,0.519606781 L3.26960678,3.51960678 Z" id="Triangle-3" fill="#FFFFFF"></path> + </g> + <g id="Line-+-Triangle-3-Copy" transform="translate(22.681981, 15.181981) scale(-1, 1) rotate(-45.000000) translate(-22.681981, -15.181981) translate(19.181981, 13.181981)" sketch:type="MSShapeGroup"> + <path d="M6.53553391,2 L3,2" id="Line" stroke="#FFFFFF" stroke-linecap="square"></path> + <path d="M3.375,0.5 L3.375,3.5 L0.375,2 L3.375,0.5 Z" id="Triangle-3" fill="#FFFFFF"></path> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/hangup.svg b/src/skins/vector/img/hangup.svg new file mode 100644 index 0000000000..be038d2b30 --- /dev/null +++ b/src/skins/vector/img/hangup.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="25px" height="26px" viewBox="-1 -1 25 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.3 (16618) - http://www.bohemiancoding.com/sketch --> + <title>Fill 72 + Path 98</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -719.000000)" stroke="#FF0064"> + <g id="Fill-72-+-Path-98" sketch:type="MSLayerGroup" transform="translate(910.000000, 719.000000)"> + <path d="M17.8404444,24 C15.8137778,24 10.8875556,21.408 6.75022222,15.8448889 C2.88088889,10.6413333 1,6.88222222 1,4.35244444 C1,2.36088889 2.37511111,1.41022222 3.11422222,0.9 L3.29644444,0.772888889 C4.11288889,0.188888889 5.38222222,0 5.86888889,0 C6.72222222,0 7.08177778,0.499555556 7.29955556,0.935111111 C7.48488889,1.30311111 9.01777778,4.59511111 9.17288889,5.00444444 C9.41111111,5.63377778 9.33288889,6.55111111 8.596,7.07822222 L8.46622222,7.16844444 C8.10044444,7.42222222 7.42,7.89333333 7.32577778,8.46622222 C7.28,8.74488889 7.37333333,9.03644444 7.61111111,9.35688889 C8.79777778,10.956 12.5862222,15.6506667 13.2693333,16.2884444 C13.8044444,16.7884444 14.4826667,16.8595556 14.9444444,16.4702222 C15.4222222,16.0675556 15.6342222,15.8297778 15.6364444,15.8271111 L15.6857778,15.7795556 C15.7257778,15.7457778 16.0991111,15.4497778 16.7093333,15.4497778 C17.1497778,15.4497778 17.5973333,15.6017778 18.04,15.9008889 C19.1884444,16.6768889 21.7808889,18.4106667 21.7808889,18.4106667 L21.8226667,18.4426667 C22.1542222,18.7266667 22.6333333,19.5453333 22.0751111,20.6106667 C21.496,21.7168889 19.6986667,24 17.8404444,24 L17.8404444,24 Z" id="Fill-72" sketch:type="MSShapeGroup"></path> + <path d="M19.8035085,4 L0,22.8035085" id="Path-98" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/leave.svg b/src/skins/vector/img/leave.svg new file mode 100644 index 0000000000..1acbe59313 --- /dev/null +++ b/src/skins/vector/img/leave.svg @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-1 -1 20 26" + xml:space="preserve"> +<g id="Page-1" sketch:type="MSPage"> + <g id="Exit-Copy-6" sketch:type="MSLayerGroup"> + <path id="Rectangle-140_1_" sketch:type="MSShapeGroup" fill="none" stroke="#76CFA6" d="M4.4,0.5h7.5c2.2,0,4,1.8,4,4v14c0,2.2-1.8,4-4,4H4.4 + c-2.2,0-4-1.8-4-4v-14C0.4,2.3,2.2,0.5,4.4,0.5z"/> + <g id="Rectangle-140_2_"> + <g> + <path fill="#76CFA6" d="M12,23H4.5C2,23,0,21,0,18.5v-14C0,2,2,0,4.5,0H12c2.5,0,4.5,2,4.5,4.5v14C16.4,21,14.4,23,12,23z M4.4,1 + C2.5,1,0.9,2.6,0.9,4.5v14c0,1.9,1.6,3.5,3.5,3.5h7.5c1.9,0,3.5-1.6,3.5-3.5v-14c0-1.9-1.6-3.5-3.5-3.5H4.4z"/> + <g> + <path fill="none" stroke="#76CFA6" d="M3.2,20.6l8.3-1.8c1.1-0.2,2-1,2-1.7V5.7c0-0.7-0.9-1.4-2-1.7L3.2,2.2"/> + </g> + </g> + </g> + <circle id="Oval-605" sketch:type="MSShapeGroup" fill="#76CFA6" cx="10.7" cy="11.1" r="1"> + </circle> + </g> +</g> +</svg> diff --git a/src/skins/vector/img/list-close.png b/src/skins/vector/img/list-close.png new file mode 100644 index 0000000000..82b322f9d4 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 0000000000..eb60864e2c --- /dev/null +++ b/src/skins/vector/img/list-close.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.2 (15857) - http://www.bohemiancoding.com/sketch --> + <title>Slice 1</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <path d="M0,5 L20,5 L10,15 L0,5 Z" id="Triangle-1" fill="#76CFA6" sketch:type="MSShapeGroup" transform="translate(10.000000, 10.000000) rotate(-90.000000) translate(-10.000000, -10.000000) "></path> + </g> +</svg> \ 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 0000000000..f8c8063197 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 0000000000..a682ec9051 --- /dev/null +++ b/src/skins/vector/img/list-open.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.2 (15857) - http://www.bohemiancoding.com/sketch --> + <title>Slice 1</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <path d="M0,5 L20,5 L10,15 L0,5 Z" id="Triangle-1" fill="#76CFA6" sketch:type="MSShapeGroup"></path> + </g> +</svg> \ 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 0000000000..cbbd289dcf 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 6526f2362e..b5e5875768 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 0000000000..0f115966ab --- /dev/null +++ b/src/skins/vector/img/members.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="17px" height="22px" viewBox="0 0 17 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_people</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-978.000000, -32.000000)" fill="#76CFA6"> + <g id="icons_people" sketch:type="MSLayerGroup" transform="translate(978.000000, 32.000000)"> + <path d="M7.79396172,11.1845854 C10.8910264,11.1845854 13.4110283,8.6755122 13.4110283,5.59121951 C13.4110283,2.50853659 10.8910264,0 7.79396172,0 C4.69851351,0 2.18012797,2.50853659 2.18012797,5.59121951 C2.18012797,8.6755122 4.69851351,11.1845854 7.79396172,11.1845854 L7.79396172,11.1845854 Z M7.79396172,0.804878049 C10.4454327,0.804878049 12.6028173,2.95229268 12.6028173,5.59121951 C12.6028173,8.2317561 10.4454327,10.3797073 7.79396172,10.3797073 C5.14410719,10.3797073 2.98833899,8.2317561 2.98833899,5.59121951 C2.98833899,2.95229268 5.14410719,0.804878049 7.79396172,0.804878049 L7.79396172,0.804878049 Z M0.885806936,21.4634146 C1.33849039,19.4978231 2.46183493,15.4298815 3.13651101,14.4663415 C4.32835286,12.76 6.45287023,12.8072195 6.48358225,12.8034634 L9.03699027,12.8013171 C9.06393064,12.7980976 11.7369539,12.5421463 12.9228689,14.5377073 C12.9320286,14.5532683 12.9422659,14.5682927 12.9535809,14.5827805 C13.6376414,15.4425239 14.755176,19.1307382 15.2598605,21.1908293 L15.2598605,21.1908293 L14.9096797,21.1908293 L1.53746094,21.1908293 L1.15172387,21.1908293 L1.15172387,21.9957073 L1.53746094,21.9957073 L14.9096797,21.9957073 L15.2954167,21.9957073 L15.2954167,21.3375005 C15.3143567,21.416496 15.3322794,21.4927245 15.3491183,21.5659024 C15.3922229,21.7520976 15.5587144,21.8781951 15.7429865,21.8781951 C15.7726209,21.8781951 15.8027941,21.8749756 15.8335061,21.8685366 C16.0506455,21.8186341 16.1869638,21.6029268 16.1368547,21.3856098 C15.7844747,19.8558049 14.5355192,15.312 13.6044602,14.1052195 C12.144831,11.6825366 9.08063366,11.9857073 8.99388568,11.9985854 L6.49759124,11.9985854 C6.38767454,11.996439 3.92209212,11.9320488 2.47323917,14.0059512 C1.66987741,15.1531707 0.489889321,19.5553171 0.0750076642,21.3872195 C0.0254373883,21.6045366 0.162294454,21.8197073 0.379972623,21.8685366 C0.460649718,21.8870317 0.541104777,21.8798767 0.61291652,21.8524806 L0.61291652,22 L1.15172387,22 L1.15172387,21.4634146 L0.885804128,21.4634146 Z" id="Fill-92" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ 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 0000000000..847baf98f9 --- /dev/null +++ b/src/skins/vector/img/mod.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="16px" height="17px" viewBox="-1 -1 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4.4 (395) - http://www.bohemiancoding.com/sketch --> + <title>icons_admin</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_19-Room-contextual-menu-hover" sketch:type="MSArtboardGroup" transform="translate(-1000.000000, -172.000000)" stroke="#FFFFFF" fill="#C2C5AF"> + <g id="people_open" sketch:type="MSLayerGroup" transform="translate(966.000000, 59.000000)"> + <g id="icons_admin" transform="translate(35.000000, 114.000000)" sketch:type="MSShapeGroup"> + <path d="M0.441894529,1.80537109 C2.59277353,3.03442388 4.25305977,2.17675781 5.9832796,0.805371094 C8.01666135,2.17675787 9.50756797,3.12670903 11.6293941,1.80537109 C11.6293941,7.01538067 11.9379879,12.2253912 5.9832796,12.2253906 C0.0285712975,12.2253901 0.441894531,7.01538067 0.441894529,1.80537109 Z" id="Path-2-Copy-2"></path> + </g> + </g> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/newmessages.png b/src/skins/vector/img/newmessages.png index e6dbeb17fd..a22156ab21 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 0000000000..a2ffca9020 --- /dev/null +++ b/src/skins/vector/img/newmessages.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icon_newmessages</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_7-Chat-new-messages" sketch:type="MSArtboardGroup" transform="translate(-244.000000, -665.000000)"> + <g id="icon_newmessages" sketch:type="MSLayerGroup" transform="translate(244.000000, 665.000000)"> + <circle id="Oval-1909" fill="#FF0064" sketch:type="MSShapeGroup" cx="12" cy="12" r="12"></circle> + <path d="M16.3400426,11.526774 C16.4870048,11.526774 16.633967,11.4708978 16.7464849,11.3583798 C16.9699899,11.1341094 16.9699899,10.7712964 16.7464849,10.547026 L12.3659396,6.16801148 C12.1424345,5.94374103 11.7788562,5.94450646 11.5545857,6.16724605 L7.16868234,10.5401371 C6.94441188,10.7644076 6.94364646,11.127986 7.16715148,11.3522564 C7.3906565,11.5772923 7.75423488,11.5772923 7.97850533,11.3537873 L11.3915495,7.95069367 L11.3930804,17.4259289 C11.3930804,17.7428161 11.6510296,18 11.9671515,18 C12.2848042,18 12.5412226,17.7428161 12.5412226,17.4259289 L12.5396917,7.9652368 L15.9343656,11.3583798 C16.0461182,11.4708978 16.1930804,11.526774 16.3400426,11.526774 L16.3400426,11.526774 Z" fill="#FFFFFF" sketch:type="MSShapeGroup" transform="translate(11.957057, 12.000000) rotate(-180.000000) translate(-11.957057, -12.000000) "></path> + </g> + </g> + </g> +</svg> \ 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 0000000000..e1d59ec6f4 --- /dev/null +++ b/src/skins/vector/img/plus.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="14px" height="14px" viewBox="-1 -1 13 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Line + Line</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="Line-+-Line" sketch:type="MSLayerGroup" transform="translate(2.000000, 2.000000)" stroke="#4A4A4A" stroke-width="2.82" stroke-linecap="square"> + <path d="M4,0.228763834 L4,7.77123617" id="Line" sketch:type="MSShapeGroup"></path> + <path d="M0.228763834,4 L7.77123617,4" id="Line" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/scrolldown.svg b/src/skins/vector/img/scrolldown.svg new file mode 100644 index 0000000000..d6599c5fc7 --- /dev/null +++ b/src/skins/vector/img/scrolldown.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icon_newmessages</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_7-Chat-new-messages" sketch:type="MSArtboardGroup" transform="translate(-809.000000, -325.000000)"> + <g id="icon_newmessages" sketch:type="MSLayerGroup" transform="translate(809.000000, 325.000000)"> + <circle id="Oval-1909" fill-opacity="0.5" fill="#454545" sketch:type="MSShapeGroup" cx="12" cy="12" r="12"></circle> + <path d="M16.3400426,11.526774 C16.4870048,11.526774 16.633967,11.4708978 16.7464849,11.3583798 C16.9699899,11.1341094 16.9699899,10.7712964 16.7464849,10.547026 L12.3659396,6.16801148 C12.1424345,5.94374103 11.7788562,5.94450646 11.5545857,6.16724605 L7.16868234,10.5401371 C6.94441188,10.7644076 6.94364646,11.127986 7.16715148,11.3522564 C7.3906565,11.5772923 7.75423488,11.5772923 7.97850533,11.3537873 L11.3915495,7.95069367 L11.3930804,17.4259289 C11.3930804,17.7428161 11.6510296,18 11.9671515,18 C12.2848042,18 12.5412226,17.7428161 12.5412226,17.4259289 L12.5396917,7.9652368 L15.9343656,11.3583798 C16.0461182,11.4708978 16.1930804,11.526774 16.3400426,11.526774 L16.3400426,11.526774 Z" fill="#FFFFFF" sketch:type="MSShapeGroup" transform="translate(11.957057, 12.000000) rotate(-180.000000) translate(-11.957057, -12.000000) "></path> + </g> + </g> + </g> +</svg> \ 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 0000000000..1692f2a6c0 --- /dev/null +++ b/src/skins/vector/img/scrollup.svg @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24px" + height="24px" + viewBox="0 0 24 24" + version="1.1" + id="svg2" + inkscape:version="0.48.4 r9939" + sodipodi:docname="scrollup.svg"> + <metadata + id="metadata18"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1058" + id="namedview16" + showgrid="false" + inkscape:zoom="9.8333333" + inkscape:cx="12.20339" + inkscape:cy="12" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title + id="title4">icon_newmessages</title> + <desc + id="desc6">Created with Sketch.</desc> + <defs + id="defs8" /> + <g + id="02-Chat" + sketch:type="MSPage" + transform="matrix(1,0,0,-1,0,24)" + style="fill:none;stroke:none"> + <g + id="02_7-Chat-new-messages" + sketch:type="MSArtboardGroup" + transform="translate(-809,-325)"> + <g + id="icon_newmessages" + sketch:type="MSLayerGroup" + transform="translate(809,325)"> + <circle + id="Oval-1909" + sketch:type="MSShapeGroup" + cx="12" + cy="12" + r="12" + d="M 24,12 C 24,18.627417 18.627417,24 12,24 5.372583,24 0,18.627417 0,12 0,5.372583 5.372583,0 12,0 18.627417,0 24,5.372583 24,12 z" + sodipodi:cx="12" + sodipodi:cy="12" + sodipodi:rx="12" + sodipodi:ry="12" + style="fill:#454545;fill-opacity:0.5" /> + <path + d="m 16.340043,11.526774 c 0.146962,0 0.293924,-0.05588 0.406442,-0.168394 0.223505,-0.224271 0.223505,-0.587084 0,-0.811354 L 12.36594,6.1680115 C 12.142435,5.943741 11.778856,5.9445065 11.554586,6.167246 l -4.3859037,4.372891 c -0.2242704,0.224271 -0.2250358,0.587849 -0.00153,0.812119 0.223505,0.225036 0.5870834,0.225036 0.8113538,0.0015 l 3.4130447,-3.4030933 0.0015,9.4752353 c 0,0.316887 0.25795,0.574071 0.574072,0.574071 0.317652,0 0.574071,-0.257184 0.574071,-0.574071 l -0.0015,-9.4606922 3.394674,3.3931432 c 0.111752,0.112518 0.258714,0.168394 0.405677,0.168394 l 0,0 z" + sketch:type="MSShapeGroup" + transform="matrix(-1,0,0,-1,23.914114,24)" + id="path14" + inkscape:connector-curvature="0" + style="fill:#ffffff" /> + </g> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/search-button.svg b/src/skins/vector/img/search-button.svg new file mode 100644 index 0000000000..f4808842ff --- /dev/null +++ b/src/skins/vector/img/search-button.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="21px" height="19px" viewBox="-8 -8 37 37" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icon_search</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="icon_search" sketch:type="MSLayerGroup" transform="translate(-8.000000, -8.000000)" stroke="#FFFFFF"> + <path d="M21.328421,19.3333333 L27.5542961,24.7357275 C27.9574623,25.085568 28.0116667,25.70516 27.6700827,26.1261351 L27.6700827,26.1261351 C27.3308636,26.5441955 26.72562,26.5965299 26.3258751,26.2496583 L20.1,20.8472641" id="Rectangle-9" sketch:type="MSShapeGroup"></path> + <g id="search" transform="translate(15.617851, 15.853553) rotate(-45.000000) translate(-15.617851, -15.853553) translate(8.117851, 7.853553)" sketch:type="MSShapeGroup"> + <ellipse id="Search" cx="7.64433504" cy="7.90518519" rx="7.1665641" ry="7.41111111"></ellipse> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/search.png b/src/skins/vector/img/search.png index d2c99855d0..2f98d29048 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 0000000000..bd4cd9200c --- /dev/null +++ b/src/skins/vector/img/search.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="21px" height="19px" viewBox="0 0 21 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_search</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -34.000000)" stroke="#76CFA6"> + <g id="icons_search" sketch:type="MSLayerGroup" transform="translate(906.000000, 30.000000)"> + <path d="M17.328421,15.3333333 L23.5542961,20.7357275 C23.9574623,21.085568 24.0116667,21.70516 23.6700827,22.1261351 L23.6700827,22.1261351 C23.3308636,22.5441955 22.72562,22.5965299 22.3258751,22.2496583 L16.1,16.8472641" id="Rectangle-9" sketch:type="MSShapeGroup"></path> + <g id="search" transform="translate(11.617851, 11.853553) rotate(-45.000000) translate(-11.617851, -11.853553) translate(4.117851, 3.853553)" sketch:type="MSShapeGroup"> + <ellipse id="Search" cx="7.64433504" cy="7.90518519" rx="7.1665641" ry="7.41111111"></ellipse> + </g> + </g> + </g> + </g> +</svg> \ 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 0000000000..8931cba75f 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 3be13bc712..cb2e0a62d0 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 0000000000..c9587d58c2 --- /dev/null +++ b/src/skins/vector/img/settings-big.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="26px" height="26px" viewBox="-1 -1 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4 (381) - http://www.bohemiancoding.com/sketch --> + <title>icons_settings</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="01-Sign-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="01_2-Sign-up-2" sketch:type="MSArtboardGroup" transform="translate(-14.000000, -720.000000)" fill="#454545" opacity="0.9"> + <g id="Left-panel" sketch:type="MSLayerGroup" transform="translate(-4.000000, 0.000000)"> + <g id="right_bottom" transform="translate(0.000000, 626.000000)" sketch:type="MSShapeGroup"> + <g id="Group-Copy-29" transform="translate(18.000000, 27.000000)"> + <path d="M16,79 C16,77.8958278 15.6093789,76.9531289 14.828125,76.171875 C14.0468711,75.3906211 13.1041722,75 12,75 C10.8958278,75 9.95312891,75.3906211 9.171875,76.171875 C8.39062109,76.9531289 8,77.8958278 8,79 C8,80.1041722 8.39062109,81.0468711 9.171875,81.828125 C9.95312891,82.6093789 10.8958278,83 12,83 C13.1041722,83 14.0468711,82.6093789 14.828125,81.828125 C15.6093789,81.0468711 16,80.1041722 16,79 L16,79 Z M24,77.296875 L24,80.765625 C24,80.8906256 23.9583338,81.0104161 23.875,81.125 C23.7916662,81.2395839 23.6875006,81.3072916 23.5625,81.328125 L20.671875,81.765625 C20.4739573,82.3281278 20.2708344,82.8020814 20.0625,83.1875 C20.4270852,83.7083359 20.9843712,84.4270787 21.734375,85.34375 C21.8385422,85.4687506 21.890625,85.5989577 21.890625,85.734375 C21.890625,85.8697923 21.8437505,85.9895828 21.75,86.09375 C21.4687486,86.4791686 20.9531288,87.041663 20.203125,87.78125 C19.4531212,88.520837 18.9635428,88.890625 18.734375,88.890625 C18.6093744,88.890625 18.4739591,88.8437505 18.328125,88.75 L16.171875,87.0625 C15.7135394,87.3020845 15.2395858,87.4999992 14.75,87.65625 C14.5833325,89.0729238 14.4322923,90.0416641 14.296875,90.5625 C14.223958,90.8541681 14.0364598,91 13.734375,91 L10.265625,91 C10.1197909,91 9.99218805,90.9557296 9.8828125,90.8671875 C9.77343695,90.7786454 9.71354172,90.6666673 9.703125,90.53125 L9.265625,87.65625 C8.75520578,87.4895825 8.28646047,87.2968761 7.859375,87.078125 L5.65625,88.75 C5.55208281,88.8437505 5.42187578,88.890625 5.265625,88.890625 C5.11979094,88.890625 4.98958391,88.8333339 4.875,88.71875 C3.56249344,87.5312441 2.70312703,86.6562528 2.296875,86.09375 C2.22395797,85.9895828 2.1875,85.8697923 2.1875,85.734375 C2.1875,85.6093744 2.22916625,85.4895839 2.3125,85.375 C2.46875078,85.1562489 2.73437313,84.8098982 3.109375,84.3359375 C3.48437688,83.8619768 3.76562406,83.494793 3.953125,83.234375 C3.67187359,82.7135391 3.45833406,82.1979192 3.3125,81.6875 L0.453125,81.265625 C0.317707656,81.2447916 0.20833375,81.179688 0.125,81.0703125 C0.04166625,80.960937 0,80.8385423 0,80.703125 L0,77.234375 C0,77.1093744 0.04166625,76.9895839 0.125,76.875 C0.20833375,76.7604161 0.307291094,76.6927084 0.421875,76.671875 L3.328125,76.234375 C3.47395906,75.7552059 3.67708203,75.2760441 3.9375,74.796875 C3.52083125,74.203122 2.96354516,73.4843792 2.265625,72.640625 C2.16145781,72.5156244 2.109375,72.3906256 2.109375,72.265625 C2.109375,72.1614578 2.15624953,72.0416673 2.25,71.90625 C2.52083469,71.5312481 3.03385039,70.9713579 3.7890625,70.2265625 C4.54427461,69.4817671 5.03645719,69.109375 5.265625,69.109375 C5.40104234,69.109375 5.53645766,69.1614578 5.671875,69.265625 L7.828125,70.9375 C8.28646063,70.6979155 8.76041422,70.5000008 9.25,70.34375 C9.4166675,68.9270762 9.56770766,67.9583359 9.703125,67.4375 C9.77604203,67.1458319 9.96354016,67 10.265625,67 L13.734375,67 C13.8802091,67 14.007812,67.0442704 14.1171875,67.1328125 C14.226563,67.2213546 14.2864583,67.3333327 14.296875,67.46875 L14.734375,70.34375 C15.2447942,70.5104175 15.7135395,70.7031239 16.140625,70.921875 L18.359375,69.25 C18.4531255,69.1562495 18.5781242,69.109375 18.734375,69.109375 C18.8697923,69.109375 18.9999994,69.1614578 19.125,69.265625 C20.4687567,70.5052145 21.3281231,71.3906223 21.703125,71.921875 C21.776042,72.0052088 21.8125,72.1197909 21.8125,72.265625 C21.8125,72.3906256 21.7708338,72.5104161 21.6875,72.625 C21.5312492,72.8437511 21.2656269,73.1901018 20.890625,73.6640625 C20.5156231,74.1380232 20.2343759,74.505207 20.046875,74.765625 C20.3177097,75.2864609 20.5312492,75.7968725 20.6875,76.296875 L23.546875,76.734375 C23.6822923,76.7552084 23.7916662,76.820312 23.875,76.9296875 C23.9583338,77.039063 24,77.1614577 24,77.296875 L24,77.296875 Z" id="icons_settings"></path> + </g> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/settings.png b/src/skins/vector/img/settings.png index 445a3909e4..264b3c9bc3 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 0000000000..4190c7b8de --- /dev/null +++ b/src/skins/vector/img/settings.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icon_settings_small</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="06a-Room-settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="06a_1-Room-settings-hover" sketch:type="MSArtboardGroup" transform="translate(-452.000000, -27.000000)" fill="#76CFA6"> + <path d="M460,33 C460,32.4479139 459.804689,31.9765645 459.414062,31.5859375 C459.023436,31.1953105 458.552086,31 458,31 C457.447914,31 456.976564,31.1953105 456.585938,31.5859375 C456.195311,31.9765645 456,32.4479139 456,33 C456,33.5520861 456.195311,34.0234355 456.585938,34.4140625 C456.976564,34.8046895 457.447914,35 458,35 C458.552086,35 459.023436,34.8046895 459.414062,34.4140625 C459.804689,34.0234355 460,33.5520861 460,33 L460,33 Z M464,32.1484375 L464,33.8828125 C464,33.9453128 463.979167,34.005208 463.9375,34.0625 C463.895833,34.119792 463.84375,34.1536458 463.78125,34.1640625 L462.335938,34.3828125 C462.236979,34.6640639 462.135417,34.9010407 462.03125,35.09375 C462.213543,35.354168 462.492186,35.7135394 462.867188,36.171875 C462.919271,36.2343753 462.945312,36.2994788 462.945312,36.3671875 C462.945312,36.4348962 462.921875,36.4947914 462.875,36.546875 C462.734374,36.7395843 462.476564,37.0208315 462.101562,37.390625 C461.726561,37.7604185 461.481771,37.9453125 461.367188,37.9453125 C461.304687,37.9453125 461.23698,37.9218752 461.164062,37.875 L460.085938,37.03125 C459.85677,37.1510423 459.619793,37.2499996 459.375,37.328125 C459.291666,38.0364619 459.216146,38.520832 459.148438,38.78125 C459.111979,38.9270841 459.01823,39 458.867188,39 L457.132812,39 C457.059895,39 456.996094,38.9778648 456.941406,38.9335938 C456.886718,38.8893227 456.856771,38.8333337 456.851562,38.765625 L456.632812,37.328125 C456.377603,37.2447912 456.14323,37.148438 455.929688,37.0390625 L454.828125,37.875 C454.776041,37.9218752 454.710938,37.9453125 454.632812,37.9453125 C454.559895,37.9453125 454.494792,37.916667 454.4375,37.859375 C453.781247,37.265622 453.351564,36.8281264 453.148438,36.546875 C453.111979,36.4947914 453.09375,36.4348962 453.09375,36.3671875 C453.09375,36.3046872 453.114583,36.244792 453.15625,36.1875 C453.234375,36.0781245 453.367187,35.9049491 453.554688,35.6679688 C453.742188,35.4309884 453.882812,35.2473965 453.976562,35.1171875 C453.835937,34.8567695 453.729167,34.5989596 453.65625,34.34375 L452.226562,34.1328125 C452.158854,34.1223958 452.104167,34.089844 452.0625,34.0351562 C452.020833,33.9804685 452,33.9192712 452,33.8515625 L452,32.1171875 C452,32.0546872 452.020833,31.994792 452.0625,31.9375 C452.104167,31.880208 452.153646,31.8463542 452.210938,31.8359375 L453.664062,31.6171875 C453.73698,31.377603 453.838541,31.138022 453.96875,30.8984375 C453.760416,30.601561 453.481773,30.2421896 453.132812,29.8203125 C453.080729,29.7578122 453.054688,29.6953128 453.054688,29.6328125 C453.054688,29.5807289 453.078125,29.5208337 453.125,29.453125 C453.260417,29.2656241 453.516925,28.9856789 453.894531,28.6132812 C454.272137,28.2408836 454.518229,28.0546875 454.632812,28.0546875 C454.700521,28.0546875 454.768229,28.0807289 454.835938,28.1328125 L455.914062,28.96875 C456.14323,28.8489577 456.380207,28.7500004 456.625,28.671875 C456.708334,27.9635381 456.783854,27.479168 456.851562,27.21875 C456.888021,27.0729159 456.98177,27 457.132812,27 L458.867188,27 C458.940105,27 459.003906,27.0221352 459.058594,27.0664062 C459.113282,27.1106773 459.143229,27.1666663 459.148438,27.234375 L459.367188,28.671875 C459.622397,28.7552088 459.85677,28.851562 460.070312,28.9609375 L461.179688,28.125 C461.226563,28.0781248 461.289062,28.0546875 461.367188,28.0546875 C461.434896,28.0546875 461.5,28.0807289 461.5625,28.1328125 C462.234378,28.7526073 462.664062,29.1953112 462.851562,29.4609375 C462.888021,29.5026044 462.90625,29.5598955 462.90625,29.6328125 C462.90625,29.6953128 462.885417,29.755208 462.84375,29.8125 C462.765625,29.9218755 462.632813,30.0950509 462.445312,30.3320312 C462.257812,30.5690116 462.117188,30.7526035 462.023438,30.8828125 C462.158855,31.1432305 462.265625,31.3984362 462.34375,31.6484375 L463.773438,31.8671875 C463.841146,31.8776042 463.895833,31.910156 463.9375,31.9648438 C463.979167,32.0195315 464,32.0807288 464,32.1484375 L464,32.1484375 Z" id="icon_settings_small" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/sound-indicator.svg b/src/skins/vector/img/sound-indicator.svg new file mode 100644 index 0000000000..9b8de53d81 --- /dev/null +++ b/src/skins/vector/img/sound-indicator.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="23px" height="20px" viewBox="-1 -1 23 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.3 (16618) - http://www.bohemiancoding.com/sketch --> + <title>sound_indicator</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-247.000000, -668.000000)" fill="#FFFFFF"> + <g id="sound_indicator" sketch:type="MSLayerGroup" transform="translate(247.000000, 668.000000)"> + <path d="M5.769,6.7284 L7.8,4.6968 L7.8,13.0626 L5.4174,10.68 L1.2,10.68 L1.2,7.08 L5.4174,7.08 L5.769,6.7284 L5.769,6.7284 Z M0,5.88 L0,11.88 L4.9206,11.88 L9,15.9594 L9,1.8 L4.9206,5.88 L0,5.88 L0,5.88 Z" id="Fill-28" sketch:type="MSShapeGroup"></path> + <path d="M17.1672,0 L16.2,0.72 C17.9172,3.021 18.9354,5.8728 18.9354,8.9586 C18.9354,12.0438 17.9172,14.8956 16.2,17.196 L17.1672,17.9166 C19.0314,15.417 20.1354,12.3168 20.1354,8.9586 C20.1354,5.6004 19.0314,2.4996 17.1672,0" id="Fill-29" sketch:type="MSShapeGroup"></path> + <path d="M14.1606,15.9948 C15.579,14.094 16.4304,11.7462 16.4304,9.1974 C16.4304,6.6486 15.579,4.3008 14.1606,2.4 L13.2,3.1152 C14.469,4.8156 15.2304,6.9168 15.2304,9.1974 C15.2304,11.478 14.4696,13.5786 13.2,15.2796 L14.1606,15.9948" id="Fill-30" sketch:type="MSShapeGroup"></path> + <path d="M11.7606,13.5024 C12.732,12.2016 13.314,10.5954 13.314,8.8512 C13.314,7.107 12.732,5.5002 11.7606,4.2 L10.8,4.9152 C11.6214,6.0156 12.114,7.3752 12.114,8.8512 C12.114,10.3272 11.6214,11.6862 10.8,12.7866 L11.7606,13.5024" id="Fill-31" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ 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 0000000000..6177f15f5e --- /dev/null +++ b/src/skins/vector/img/tick.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="17px" height="14px" viewBox="-1 -1 17 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: sketchtool 3.4 (381) - http://www.bohemiancoding.com/sketch --> + <title>icon_tick</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="06a-Room-settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="06_4-Room-settings-admin" sketch:type="MSArtboardGroup" transform="translate(-310.000000, -206.000000)" fill="#4A4A4A"> + <path d="M315,218 L310,213 L312,211 L315,214 L323,206 L325,208 L315,218 Z" id="icon_tick" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/trans.png b/src/skins/vector/img/trans.png new file mode 100644 index 0000000000..8ba2310a06 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 7754f58717..c11c0c452d 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 0000000000..6099c2e976 --- /dev/null +++ b/src/skins/vector/img/upload-big.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="45px" height="59px" viewBox="-1 -1 45 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_upload_drop</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="03-Input" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="03_05-File-drop" sketch:type="MSArtboardGroup" transform="translate(-570.000000, -368.000000)"> + <g id="icons_upload_drop" sketch:type="MSLayerGroup" transform="translate(570.000000, 368.000000)"> + <g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup"> + <path d="M0,4.00812931 C0,1.79450062 1.78537926,0 4.00241155,0 L24.8253683,0 C24.8253683,0 42.2466793,16.8210687 42.2466793,16.8210687 L42.2466793,53.000599 C42.2466793,55.2094072 40.4583762,57 38.2531894,57 L3.99348992,57 C1.78794634,57 0,55.1999609 0,52.9918707 L0,4.00812931 Z" id="Rectangle-5" stroke="#76CFA6"></path> + <path d="M40.5848017,19.419576 L29.8354335,19.419576 C26.7387692,19.419576 24.2284269,16.9063989 24.2284269,13.8067771 L24.2284269,4.88501382 L40.5848017,19.419576 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path> + <path d="M42.2466793,18.3870968 L29.539478,18.3870968 C26.4130381,18.3870968 23.8785579,15.8497544 23.8785579,12.7203286 L23.8785579,0" id="Rectangle-6" stroke="#76CFA6"></path> + </g> + <path d="M31.3419737,32.9284726 C31.701384,32.9284726 32.0607942,32.8000473 32.3359677,32.5414375 C32.8825707,32.0259772 32.8825707,31.1920926 32.3359677,30.6766323 L21.622922,20.6119619 C21.076319,20.0965016 20.187153,20.0982608 19.638678,20.6102026 L8.9125289,30.6607991 C8.36405391,31.1762594 8.36218198,32.0119032 8.90878504,32.5273635 C9.4553881,33.0445831 10.344554,33.0445831 10.893029,32.530882 L19.2399573,24.7092556 L19.2437012,46.487014 C19.2437012,47.2153435 19.874541,47.8064516 20.6476474,47.8064516 C21.4244976,47.8064516 22.0515936,47.2153435 22.0515936,46.487014 L22.0478497,24.7426814 L30.3498517,32.5414375 C30.6231533,32.8000473 30.9825635,32.9284726 31.3419737,32.9284726 L31.3419737,32.9284726 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> diff --git a/src/skins/vector/img/upload.png b/src/skins/vector/img/upload.png index 428501f00f..7457bcd0f1 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 0000000000..039014a2f3 --- /dev/null +++ b/src/skins/vector/img/upload.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="19px" height="24px" viewBox="-1 -1 19 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch --> + <title>icons_upload</title> + <desc>Created with bin/sketchtool.</desc> + <defs></defs> + <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-873.000000, -722.000000)"> + <g id="icons_upload" sketch:type="MSLayerGroup" transform="translate(873.000000, 722.000000)"> + <g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup"> + <path d="M0,4.00955791 C0,1.79514022 1.78163126,0 3.99825563,0 L9.59161955,0 C9.59161955,0 16.3225806,6.49234232 16.3225806,6.49234232 L16.3225806,18.0063928 C16.3225806,20.2120012 14.5290874,22 12.3296282,22 L3.99295243,22 C1.7877057,22 0,20.1996477 0,17.9904421 L0,4.00955791 Z" id="Rectangle-5" stroke="#76CFA6"></path> + <path d="M15.6804916,7.49527496 L11.5273266,7.49527496 C10.3308881,7.49527496 9.3609831,6.52527676 9.3609831,5.3289315 L9.3609831,1.88544393 L15.6804916,7.49527496 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path> + <path d="M16.3225806,7.09677419 L11.4129801,7.09677419 C10.2050375,7.09677419 9.22580645,6.11744908 9.22580645,4.90960051 L9.22580645,0" id="Rectangle-6" stroke="#76CFA6"></path> + </g> + <path d="M12.3736951,12.709235 C12.5125582,12.709235 12.6514212,12.6596674 12.7577382,12.5598531 C12.9689258,12.3609035 12.9689258,12.0390533 12.7577382,11.8401037 L8.61860697,7.95549406 C8.40741942,7.75654446 8.06387804,7.75722347 7.85196724,7.95481505 L3.70777326,11.8339926 C3.49586247,12.0329422 3.49513923,12.3554714 3.70632677,12.554421 C3.91751432,12.7540496 4.2610557,12.7540496 4.4729665,12.555779 L7.69791605,9.53690567 L7.69936254,17.9423563 C7.69936254,18.2234659 7.94309612,18.4516129 8.24179631,18.4516129 C8.541943,18.4516129 8.78423008,18.2234659 8.78423008,17.9423563 L8.78278359,9.54980684 L11.9903753,12.5598531 C12.095969,12.6596674 12.2348321,12.709235 12.3736951,12.709235 L12.3736951,12.709235 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/video-mute.svg b/src/skins/vector/img/video-mute.svg new file mode 100644 index 0000000000..6de60ba39b --- /dev/null +++ b/src/skins/vector/img/video-mute.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="31px" height="27px" viewBox="-2 -2 31 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.3 (16618) - http://www.bohemiancoding.com/sketch --> + <title>icons_video copy</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-871.000000, -667.000000)" stroke="#FFFFFF"> + <g id="icons_video-copy" sketch:type="MSLayerGroup" transform="translate(871.000000, 666.000000)"> + <g id="Rectangle-20-+-Path-16" transform="translate(0.000000, 0.464286)" sketch:type="MSShapeGroup"> + <rect id="Rectangle-20" x="0" y="0.535714286" width="20" height="20" rx="4"></rect> + <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/video-unmute.svg b/src/skins/vector/img/video-unmute.svg new file mode 100644 index 0000000000..a6c6c3b681 --- /dev/null +++ b/src/skins/vector/img/video-unmute.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="31px" height="27px" viewBox="-1 -1 31 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>icons_video copy</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-870.000000, -666.000000)" stroke="#FFFFFF"> + <g id="icons_video-copy" sketch:type="MSLayerGroup" transform="translate(870.000000, 666.000000)"> + <g id="Rectangle-20-+-Path-16" transform="translate(1.000000, 0.464286)" sketch:type="MSShapeGroup"> + <rect id="Rectangle-20" x="0" y="0.535714286" width="20" height="20" rx="4"></rect> + <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path> + </g> + <path d="M24,1 L0.00856596586,23.6585766" id="Path-98" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/voice-mute.svg b/src/skins/vector/img/voice-mute.svg new file mode 100644 index 0000000000..336641078e --- /dev/null +++ b/src/skins/vector/img/voice-mute.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="21px" height="26px" viewBox="-4 -1 21 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.3 (16618) - http://www.bohemiancoding.com/sketch --> + <title>Audio</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-915.000000, -665.000000)" fill="#FFFFFF"> + <g id="Audio" sketch:type="MSLayerGroup" transform="translate(915.000000, 665.000000)"> + <path d="M3.51108197,4.57927869 C3.51108197,2.62701639 5.09940984,1.03868852 7.05206557,1.03868852 C9.00472131,1.03868852 10.5930492,2.62701639 10.5930492,4.57927869 L10.5930492,12.448918 C10.5930492,14.4015738 9.00472131,15.9899016 7.05206557,15.9899016 C5.09940984,15.9899016 3.51108197,14.4015738 3.51108197,12.448918 L3.51108197,4.57927869 L3.51108197,4.57927869 Z M7.05206557,16.7767869 C9.44183607,16.7767869 11.3799344,14.839082 11.3799344,12.448918 L11.3799344,4.57927869 C11.3799344,2.18911475 9.44183607,0.251803279 7.05206557,0.251803279 C4.66229508,0.251803279 2.72419672,2.18911475 2.72419672,4.57927869 L2.72419672,12.448918 C2.72419672,14.839082 4.66229508,16.7767869 7.05206557,16.7767869 Z M12.9517377,8.51409836 L12.9517377,12.448918 C12.9517377,15.703082 10.3042623,18.3505574 7.05009836,18.3505574 C3.79554098,18.3505574 1.14845902,15.703082 1.14845902,12.448918 L1.14845902,8.51409836 L0.36157377,8.51409836 L0.36157377,12.448918 C0.36157377,16.0217705 3.17980328,18.9407213 6.70898361,19.1201311 L6.65862295,19.1201311 L6.65862295,23.080918 L3.11567213,23.080918 L3.11567213,23.8678033 L10.9845246,23.8678033 L10.9845246,23.080918 L7.4455082,23.080918 L7.4455082,19.1201311 L7.39121311,19.1201311 C10.9203934,18.9407213 13.738623,16.0217705 13.738623,12.448918 L13.738623,8.51409836 L12.9517377,8.51409836 Z" id="Fill-16" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/voice-unmute.svg b/src/skins/vector/img/voice-unmute.svg new file mode 100644 index 0000000000..0d7e6f429f --- /dev/null +++ b/src/skins/vector/img/voice-unmute.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="21px" height="26px" viewBox="-1 -1 21 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Audio</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-912.000000, -665.000000)"> + <g id="Audio" sketch:type="MSLayerGroup" transform="translate(912.000000, 665.000000)"> + <path d="M6.51108197,4.57927869 C6.51108197,2.62701639 8.09940984,1.03868852 10.0520656,1.03868852 C12.0047213,1.03868852 13.5930492,2.62701639 13.5930492,4.57927869 L13.5930492,12.448918 C13.5930492,14.4015738 12.0047213,15.9899016 10.0520656,15.9899016 C8.09940984,15.9899016 6.51108197,14.4015738 6.51108197,12.448918 L6.51108197,4.57927869 L6.51108197,4.57927869 Z M10.0520656,16.7767869 C12.4418361,16.7767869 14.3799344,14.839082 14.3799344,12.448918 L14.3799344,4.57927869 C14.3799344,2.18911475 12.4418361,0.251803279 10.0520656,0.251803279 C7.66229508,0.251803279 5.72419672,2.18911475 5.72419672,4.57927869 L5.72419672,12.448918 C5.72419672,14.839082 7.66229508,16.7767869 10.0520656,16.7767869 Z M15.9517377,8.51409836 L15.9517377,12.448918 C15.9517377,15.703082 13.3042623,18.3505574 10.0500984,18.3505574 C6.79554098,18.3505574 4.14845902,15.703082 4.14845902,12.448918 L4.14845902,8.51409836 L3.36157377,8.51409836 L3.36157377,12.448918 C3.36157377,16.0217705 6.17980328,18.9407213 9.70898361,19.1201311 L9.65862295,19.1201311 L9.65862295,23.080918 L6.11567213,23.080918 L6.11567213,23.8678033 L13.9845246,23.8678033 L13.9845246,23.080918 L10.4455082,23.080918 L10.4455082,19.1201311 L10.3912131,19.1201311 C13.9203934,18.9407213 16.738623,16.0217705 16.738623,12.448918 L16.738623,8.51409836 L15.9517377,8.51409836 Z" id="Fill-16" fill="#FFFFFF" sketch:type="MSShapeGroup"></path> + <path d="M19,3 L0.272141024,20.6874224" id="Path-98" stroke="#FFFFFF" sketch:type="MSShapeGroup"></path> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/voice.png b/src/skins/vector/img/voice.png new file mode 100644 index 0000000000..5ba765b0f4 Binary files /dev/null and b/src/skins/vector/img/voice.png differ diff --git a/src/skins/vector/img/voice.svg b/src/skins/vector/img/voice.svg new file mode 100644 index 0000000000..ff87270ba5 --- /dev/null +++ b/src/skins/vector/img/voice.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="16px" height="26px" viewBox="0 0 16 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.2 (15857) - http://www.bohemiancoding.com/sketch --> + <title>icon_voice</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="icon_voice" sketch:type="MSLayerGroup" fill="#76CFA6"> + <path d="M15.690213,10.5130204 C15.690213,9.68172789 15.0096307,9.00560998 14.1739648,9.00560998 C13.3355096,9.00560998 12.6543693,9.68172789 12.6543693,10.5130204 L12.6554851,13.5200825 C12.6510222,13.5599846 11.9274851,17.4980943 7.84566437,17.4980943 C3.7599387,17.4980943 3.03472797,13.5815982 3.03361226,13.5500091 L3.03361226,10.5130204 C3.03361226,9.68172789 2.35302989,9.00560998 1.51680613,9.00560998 C0.680024521,9.00560998 0,9.68172789 0,10.5130204 L0,13.5500091 C0,15.5922177 1.86211801,19.602927 6.32830038,20.3771374 L6.32830038,22.9513732 L2.35358774,22.9513732 C1.51680613,22.9513732 0.836781609,23.6280454 0.836781609,24.4593379 C0.836781609,25.2900762 1.51680613,25.9661941 2.35358774,25.9661941 L13.3371831,25.9661941 C14.1717333,25.9661941 14.8506421,25.2900762 14.8506421,24.4593379 C14.8506421,23.6280454 14.1717333,22.9513732 13.3371831,22.9513732 L9.36079693,22.9513732 L9.36079693,20.3771374 C13.828095,19.602927 15.690213,15.5922177 15.690213,13.5500091 L15.690213,10.5130204 Z M13.3371831,23.7826658 C13.7098299,23.7826658 14.0138605,24.0863646 14.0138605,24.4593379 C14.0138605,24.8317569 13.7098299,25.1349016 13.3371831,25.1349016 L2.35358774,25.1349016 C1.97870958,25.1349016 1.67356322,24.8317569 1.67356322,24.4593379 C1.67356322,24.0863646 1.97870958,23.7826658 2.35358774,23.7826658 L6.74613333,23.7826658 C6.97764291,23.7826658 7.16508199,23.5970104 7.16508199,23.3670195 L7.16508199,20.0196816 C7.16508199,19.8113043 7.00888276,19.6350703 6.80024521,19.6079147 C2.63809349,19.0681288 0.836781609,15.334517 0.836781609,13.5500091 L0.836781609,10.5130204 C0.836781609,10.1400472 1.14192797,9.83690249 1.51680613,9.83690249 C1.89168429,9.83690249 2.19683065,10.1400472 2.19683065,10.5130204 L2.19683065,13.555551 C2.20519847,14.1812372 3.30696092,18.3293868 7.84566437,18.3293868 C12.2990161,18.3293868 13.491151,14.1175048 13.491151,13.5500091 L13.491151,10.5130204 C13.491151,10.1400472 13.797413,9.83690249 14.1739648,9.83690249 C14.5488429,9.83690249 14.8539893,10.1400472 14.8539893,10.5130204 L14.8539893,13.5500091 C14.8539893,15.334517 13.0532352,19.0681288 8.88940996,19.6079147 C8.68077241,19.6350703 8.52401533,19.8113043 8.52401533,20.0196816 L8.52401533,23.3670195 C8.52401533,23.5970104 8.71201226,23.7826658 8.94296398,23.7826658 L13.3371831,23.7826658 Z" id="Fill-200" sketch:type="MSShapeGroup"></path> + <path d="M3.04309579,10.1578404 L3.04309579,8.37998277 L3.04309579,7.54869025 L3.04309579,6.79997279 L3.04309579,5.96868027 L3.04309579,5.63727166 C3.04309579,5.49650612 3.05257931,5.35795737 3.06429425,5.21996281 L3.21324138,4.38867029 C3.76384368,2.3436907 5.62875096,0.831292517 7.84510651,0.831292517 C10.0609042,0.831292517 11.9263693,2.3436907 12.4775295,4.38867029 L12.6259188,5.21996281 C12.6376337,5.35795737 12.6476751,5.49650612 12.6476751,5.63727166 L12.6476751,5.96868027 L12.6476751,6.79997279 L12.6476751,7.54869025 L12.6476751,8.37998277 L12.6476751,10.1578404 C12.6476751,10.3872771 12.8351142,10.5734866 13.0660659,10.5734866 C13.2970176,10.5734866 13.4844567,10.3872771 13.4844567,10.1578404 L13.4844567,5.63727166 C13.4844567,2.52934603 10.954587,0 7.84510651,0 C4.7350682,0 2.20631418,2.52934603 2.20631418,5.63727166 L2.20631418,10.1578404 C2.20631418,10.3872771 2.39375326,10.5734866 2.62470498,10.5734866 C2.8556567,10.5734866 3.04309579,10.3872771 3.04309579,10.1578404 Z" id="Path" sketch:type="MSShapeGroup"></path> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/voip-chevron.svg b/src/skins/vector/img/voip-chevron.svg new file mode 100644 index 0000000000..5f7cbe7153 --- /dev/null +++ b/src/skins/vector/img/voip-chevron.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="22px" height="17px" viewBox="-1 -1 22 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> + <title>Triangle 1</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-912.000000, -693.000000)" fill="#76CFA6"> + <polygon id="Triangle-1" sketch:type="MSShapeGroup" transform="translate(922.000000, 700.500000) scale(1, -1) translate(-922.000000, -700.500000) " points="922 693 932 708 912 708 "></polygon> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/skins/vector/img/voip-mute.png b/src/skins/vector/img/voip-mute.png new file mode 100644 index 0000000000..a16d1001e5 Binary files /dev/null and b/src/skins/vector/img/voip-mute.png differ diff --git a/src/skins/vector/img/warning.png b/src/skins/vector/img/warning.png new file mode 100644 index 0000000000..c5553530a8 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 0000000000..b9a96a88e5 --- /dev/null +++ b/src/skins/vector/img/warning.svg @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 24 23" style="enable-background:new 0 0 24 23;" xml:space="preserve"> +<style type="text/css"> + .st0{clip-path:url(#SVGID_2_);fill:#FF0064;} + .st1{clip-path:url(#SVGID_4_);fill:#FFFFFF;} +</style> +<g> + <g> + <defs> + <path id="SVGID_1_" d="M9.2,2.2c1.6-2.9,4.1-2.9,5.7,0l8.3,15.4c1.6,2.9,0.2,5.3-3.2,5.3H4c-3.3,0-4.7-2.4-3.2-5.3L9.2,2.2 + L9.2,2.2z M9.2,2.2"/> + </defs> + <clipPath id="SVGID_2_"> + <use xlink:href="#SVGID_1_" style="overflow:visible;"/> + </clipPath> + <rect x="-4.8" y="-5" class="st0" width="33.6" height="32.9"/> + </g> + <g> + <defs> + <path id="SVGID_3_" d="M12.7,15L13,5.4H11l0.3,9.6H12.7L12.7,15z M12,19.1c0.7,0,1.2-0.5,1.2-1.2c0-0.7-0.5-1.2-1.2-1.2 + c-0.7,0-1.2,0.5-1.2,1.2C10.8,18.6,11.3,19.1,12,19.1L12,19.1L12,19.1z M12,19.1"/> + </defs> + <clipPath id="SVGID_4_"> + <use xlink:href="#SVGID_3_" style="overflow:visible;"/> + </clipPath> + <rect x="5.8" y="0.4" class="st1" width="12.4" height="23.7"/> + </g> +</g> +</svg> diff --git a/src/skins/vector/img/warning2.png b/src/skins/vector/img/warning2.png new file mode 100644 index 0000000000..db0fd4a897 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 8dba10cf30..0000000000 --- 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 287ff9e237..0000000000 --- a/src/skins/vector/skinfo.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseSkin": "" -} diff --git a/src/skins/vector/views/atoms/EditableText.js b/src/skins/vector/views/atoms/EditableText.js deleted file mode 100644 index 1848b029a6..0000000000 --- a/src/skins/vector/views/atoms/EditableText.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var EditableTextController = require('matrix-react-sdk/lib/controllers/atoms/EditableText') - -module.exports = React.createClass({ - displayName: 'EditableText', - mixins: [EditableTextController], - - onKeyUp: function(ev) { - if (ev.key == "Enter") { - this.onFinish(ev); - } else if (ev.key == "Escape") { - this.cancelEdit(); - } - }, - - onClickDiv: function() { - this.setState({ - phase: this.Phases.Edit, - }) - }, - - onFocus: function(ev) { - ev.target.setSelectionRange(0, ev.target.value.length); - }, - - onFinish: function(ev) { - if (ev.target.value) { - this.setValue(ev.target.value, ev.key === "Enter"); - } else { - this.cancelEdit(); - } - }, - - render: function() { - var editable_el; - - if (this.state.phase == this.Phases.Display) { - if (this.state.value) { - editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.state.value}</div>; - } else { - editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.props.label}</div>; - } - } else if (this.state.phase == this.Phases.Edit) { - editable_el = ( - <div> - <input type="text" defaultValue={this.state.value} onKeyUp={this.onKeyUp} onFocus={this.onFocus} onBlur={this.onFinish} placeholder={this.props.placeHolder} autoFocus/> - </div> - ); - } - - return ( - <div className="mx_EditableText"> - {editable_el} - </div> - ); - } -}); diff --git a/src/skins/vector/views/atoms/ImageView.js b/src/skins/vector/views/atoms/ImageView.js deleted file mode 100644 index 676348c04c..0000000000 --- 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 ( - <img className="mx_ImageView" src={this.props.src} style={style} /> - ); - } -}); diff --git a/src/skins/vector/views/atoms/LogoutButton.js b/src/skins/vector/views/atoms/LogoutButton.js deleted file mode 100644 index 619160f61c..0000000000 --- a/src/skins/vector/views/atoms/LogoutButton.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var LogoutButtonController = require('matrix-react-sdk/lib/controllers/atoms/LogoutButton') - -module.exports = React.createClass({ - displayName: 'LogoutButton', - mixins: [LogoutButtonController], - - render: function() { - return ( - <button className="mx_LogoutButton" onClick={this.onClick}>Sign out</button> - ); - } -}); diff --git a/src/skins/vector/views/atoms/MemberAvatar.js b/src/skins/vector/views/atoms/MemberAvatar.js deleted file mode 100644 index 69652e1a2a..0000000000 --- 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 ( - <img className="mx_MemberAvatar" src={this.state.imageUrl} - onError={this.onError} - width={this.props.width} height={this.props.height} /> - ); - } -}); diff --git a/src/skins/vector/views/atoms/MessageTimestamp.js b/src/skins/vector/views/atoms/MessageTimestamp.js deleted file mode 100644 index 98cfe4a10b..0000000000 --- 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 ( - <span className="mx_MessageTimestamp"> - { this.formatDate(date) } - </span> - ); - }, -}); - diff --git a/src/skins/vector/views/atoms/RoomAvatar.js b/src/skins/vector/views/atoms/RoomAvatar.js deleted file mode 100644 index 3b5d463498..0000000000 --- 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 ( - <img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError} - style={style} - /> - ); - } -}); diff --git a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js b/src/skins/vector/views/atoms/create_room/CreateRoomButton.js deleted file mode 100644 index 2fc9d057a0..0000000000 --- a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var CreateRoomButtonController = require('matrix-react-sdk/lib/controllers/atoms/create_room/CreateRoomButton') - -module.exports = React.createClass({ - displayName: 'CreateRoomButton', - mixins: [CreateRoomButtonController], - - render: function() { - return ( - <button className="mx_CreateRoomButton" onClick={this.onClick}>Create Room</button> - ); - } -}); diff --git a/src/skins/vector/views/atoms/create_room/Presets.js b/src/skins/vector/views/atoms/create_room/Presets.js deleted file mode 100644 index a098a7d7e8..0000000000 --- a/src/skins/vector/views/atoms/create_room/Presets.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var PresetsController = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets') - -module.exports = React.createClass({ - displayName: 'CreateRoomPresets', - mixins: [PresetsController], - - onValueChanged: function(ev) { - this.props.onChange(ev.target.value) - }, - - render: function() { - return ( - <select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}> - <option value={this.Presets.PrivateChat}>Private Chat</option> - <option value={this.Presets.PublicChat}>Public Chat</option> - <option value={this.Presets.Custom}>Custom</option> - </select> - ); - } -}); diff --git a/src/skins/vector/views/atoms/create_room/RoomAlias.js b/src/skins/vector/views/atoms/create_room/RoomAlias.js deleted file mode 100644 index 0a8cadc888..0000000000 --- a/src/skins/vector/views/atoms/create_room/RoomAlias.js +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var RoomAliasController = require('matrix-react-sdk/lib/controllers/atoms/create_room/RoomAlias') - -module.exports = React.createClass({ - displayName: 'RoomAlias', - mixins: [RoomAliasController], - - onValueChanged: function(ev) { - this.props.onChange(ev.target.value); - }, - - onFocus: function(ev) { - var target = ev.target; - var curr_val = ev.target.value; - - if (this.props.homeserver) { - if (curr_val == "") { - setTimeout(function() { - target.value = "#:" + this.props.homeserver; - target.setSelectionRange(1, 1); - }, 0); - } else { - var suffix = ":" + this.props.homeserver; - setTimeout(function() { - target.setSelectionRange( - curr_val.startsWith("#") ? 1 : 0, - curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length - ); - }, 0); - } - } - }, - - onBlur: function(ev) { - var curr_val = ev.target.value; - - if (this.props.homeserver) { - if (curr_val == "#:" + this.props.homeserver) { - ev.target.value = ""; - return; - } - - if (curr_val != "") { - var new_val = ev.target.value; - var suffix = ":" + this.props.homeserver; - if (!curr_val.startsWith("#")) new_val = "#" + new_val; - if (!curr_val.endsWith(suffix)) new_val = new_val + suffix; - ev.target.value = new_val; - } - } - }, - - render: function() { - return ( - <input type="text" className="mx_RoomAlias" placeholder="Alias (optional)" - onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur} - value={this.props.alias}/> - ); - } -}); 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 9cf28d1ba4..0000000000 --- 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 ( - <video> - </video> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/ChangeAvatar.js b/src/skins/vector/views/molecules/ChangeAvatar.js deleted file mode 100644 index 42c2d1fd45..0000000000 --- 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 = <RoomAvatar room={this.props.room} width='320' height='240' resizeMethod='scale' />; - } else { - var style = { - maxWidth: 320, - maxHeight: 240, - }; - avatarImg = <img src={this.state.avatarUrl} style={style} />; - } - - switch (this.state.phase) { - case this.Phases.Display: - case this.Phases.Error: - return ( - <div> - <div className="mx_Dialog_content"> - {avatarImg} - </div> - <div className="mx_Dialog_content"> - Upload new: - <input type="file" onChange={this.onFileSelected}/> - {this.state.errorText} - </div> - </div> - ); - case this.Phases.Uploading: - return ( - <Loader /> - ); - } - } -}); diff --git a/src/skins/vector/views/molecules/ChangeDisplayName.js b/src/skins/vector/views/molecules/ChangeDisplayName.js deleted file mode 100644 index 1a094ec24b..0000000000 --- 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 ( - <Loader /> - ); - } else if (this.state.errorString) { - return ( - <div className="error">{this.state.errorString}</div> - ); - } else { - var EditableText = sdk.getComponent('atoms.EditableText'); - return ( - <EditableText ref="displayname_edit" initialValue={this.state.displayName} label="Click to set display name." onValueChanged={this.onValueChanged}/> - ); - } - } -}); diff --git a/src/skins/vector/views/molecules/ChangePassword.js b/src/skins/vector/views/molecules/ChangePassword.js deleted file mode 100644 index 004fed3982..0000000000 --- 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 ( - <div> - <div className="mx_Dialog_content"> - <div>{this.state.errorString}</div> - <div><label>Old password <input type="password" ref="old_input"/></label></div> - <div><label>New password <input type="password" ref="new_input"/></label></div> - <div><label>Confirm password <input type="password" ref="confirm_input"/></label></div> - </div> - <div className="mx_Dialog_buttons"> - <button onClick={this.onClickChange}>Change Password</button> - <button onClick={this.props.onFinished}>Cancel</button> - </div> - </div> - ); - case this.Phases.Uploading: - return ( - <div className="mx_Dialog_content"> - <Loader /> - </div> - ); - case this.Phases.Success: - return ( - <div> - <div className="mx_Dialog_content"> - Success! - </div> - <div className="mx_Dialog_buttons"> - <button onClick={this.props.onFinished}>Ok</button> - </div> - </div> - ) - } - } -}); diff --git a/src/skins/vector/views/molecules/EventTile.js b/src/skins/vector/views/molecules/EventTile.js deleted file mode 100644 index 389b8b4012..0000000000 --- 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 = <MessageTimestamp ts={this.props.mxEvent.getTs()} /> - var editButton = ( - <input - type="image" src="img/edit.png" alt="Edit" - className="mx_EventTile_editButton" onClick={this.onEditClicked} - /> - ); - - 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 = ( - <div className="mx_EventTile_avatar"> - <MemberAvatar member={this.props.mxEvent.sender} /> - </div> - ); - } - if (EventTileType.needsSenderProfile()) { - sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />; - } - } - return ( - <div className={classes}> - { avatar } - { sender } - <div> - { timestamp } - { editButton } - <EventTileType mxEvent={this.props.mxEvent} /> - </div> - </div> - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MFileTile.js b/src/skins/vector/views/molecules/MFileTile.js deleted file mode 100644 index f7e8991f93..0000000000 --- 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 ( - <span className="mx_MFileTile"> - <div className="mx_MImageTile_download"> - <a href={cli.mxcUrlToHttp(content.url)} target="_blank"> - <img src="img/download.png" width="10" height="12"/> - Download {this.presentableTextForFile(content)} - </a> - </div> - </span> - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MImageTile.js b/src/skins/vector/views/molecules/MImageTile.js deleted file mode 100644 index ed61a39019..0000000000 --- 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 ( - <span className="mx_MImageTile"> - <a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }> - <img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 320, 240)} alt={content.body} style={imgStyle} /> - </a> - <div className="mx_MImageTile_download"> - <a href={cli.mxcUrlToHttp(content.url)} target="_blank"> - <img src="img/download.png" width="10" height="12"/> - Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" }) - </a> - </div> - </span> - ); - }, -}); diff --git a/src/skins/vector/views/molecules/MNoticeTile.js b/src/skins/vector/views/molecules/MNoticeTile.js deleted file mode 100644 index aa88612784..0000000000 --- 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 ( - <span ref="content" className="mx_MNoticeTile mx_MessageTile_content"> - {content.body} - </span> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MRoomMemberTile.js b/src/skins/vector/views/molecules/MRoomMemberTile.js deleted file mode 100644 index 0048306d39..0000000000 --- a/src/skins/vector/views/molecules/MRoomMemberTile.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var sdk = require('matrix-react-sdk') -var TextForEvent = require('matrix-react-sdk/lib/TextForEvent'); - -module.exports = React.createClass({ - displayName: 'MRoomMemberTile', - - getMemberEventText: function() { - return TextForEvent.textForEvent(this.props.mxEvent); - }, - - render: function() { - // XXX: for now, just cheekily borrow the css from message tile... - var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null; - var text = this.getMemberEventText(); - if (!text) return <div/>; - var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp'); - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( - <div className="mx_MessageTile mx_MessageTile_notice"> - <div className="mx_MessageTile_avatar"> - <MemberAvatar member={this.props.mxEvent.sender} /> - </div> - { timestamp } - <span className="mx_SenderProfile"></span> - <span className="mx_MessageTile_content"> - { text } - </span> - </div> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MTextTile.js b/src/skins/vector/views/molecules/MTextTile.js deleted file mode 100644 index 50555f949f..0000000000 --- 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 ( - <span ref="content" className="mx_MTextTile mx_MessageTile_content"> - {content.body} - </span> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MemberInfo.js b/src/skins/vector/views/molecules/MemberInfo.js deleted file mode 100644 index a2a3874ac7..0000000000 --- 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 = <div className="mx_ContextualMenu_field" onClick={this.onLeaveClick}>Leave room</div>; - } - else { - interactButton = <div className="mx_ContextualMenu_field" onClick={this.onChatClick}>Start chat</div>; - } - - if (this.state.creatingRoom) { - spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>; - } - - if (this.state.can.kick) { - kickButton = <div className="mx_ContextualMenu_field" onClick={this.onKick}> - Kick - </div>; - } - if (this.state.can.ban) { - banButton = <div className="mx_ContextualMenu_field" onClick={this.onBan}> - Ban - </div>; - } - if (this.state.can.mute) { - var muteLabel = this.state.muted ? "Unmute" : "Mute"; - muteButton = <div className="mx_ContextualMenu_field" onClick={this.onMuteToggle}> - {muteLabel} - </div>; - } - if (this.state.can.modifyLevel) { - var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod"; - giveModButton = <div className="mx_ContextualMenu_field" onClick={this.onModToggle}> - {giveOpLabel} - </div> - } - - return ( - <div> - {interactButton} - {muteButton} - {kickButton} - {banButton} - {giveModButton} - {spinner} - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/MemberTile.js b/src/skins/vector/views/molecules/MemberTile.js deleted file mode 100644 index 4c34fcb7a7..0000000000 --- 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 = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>; - } - 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 ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : 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 = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) } { this.getDuration(active) } ago</div>; - } - else { - presence = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) }</div>; - } - - nameEl = - <div className="mx_MemberTile_details"> - { leave } - <div className="mx_MemberTile_userId">{ this.props.member.userId }</div> - { presence } - </div> - } - else { - nameEl = - <div className={nameClass}> - { name } - </div> - } - - var MemberAvatar = sdk.getComponent('atoms.MemberAvatar'); - return ( - <div className={mainClassName} title={ this.getPowerLabel() } onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }> - <div className="mx_MemberTile_avatar"> - <MemberAvatar member={this.props.member} /> - { power } - </div> - { nameEl } - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/MessageComposer.js b/src/skins/vector/views/molecules/MessageComposer.js deleted file mode 100644 index c94cade5fb..0000000000 --- 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 ( - <div className="mx_MessageComposer"> - <div className="mx_MessageComposer_wrapper"> - <div className="mx_MessageComposer_row"> - <div className="mx_MessageComposer_avatar"> - <MemberAvatar member={me} /> - </div> - <div className="mx_MessageComposer_input"> - <textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message" /> - </div> - <div className="mx_MessageComposer_upload" onClick={this.onUploadClick}> - <img src="img/upload.png" width="32" height="32"/> - <input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} /> - </div> - </div> - </div> - </div> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/MessageTile.js b/src/skins/vector/views/molecules/MessageTile.js deleted file mode 100644 index f30fee92d1..0000000000 --- a/src/skins/vector/views/molecules/MessageTile.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var sdk = require('matrix-react-sdk') - -var MessageTileController = require('matrix-react-sdk/lib/controllers/molecules/MessageTile') - -module.exports = React.createClass({ - displayName: 'MessageTile', - mixins: [MessageTileController], - - statics: { - needsSenderProfile: function() { - return true; - } - }, - - render: function() { - var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile'); - - var tileTypes = { - 'm.text': sdk.getComponent('molecules.MTextTile'), - 'm.notice': sdk.getComponent('molecules.MNoticeTile'), - 'm.emote': sdk.getComponent('molecules.MEmoteTile'), - 'm.image': sdk.getComponent('molecules.MImageTile'), - 'm.file': sdk.getComponent('molecules.MFileTile') - }; - - var content = this.props.mxEvent.getContent(); - var msgtype = content.msgtype; - var TileType = UnknownMessageTile; - if (msgtype && tileTypes[msgtype]) { - TileType = tileTypes[msgtype]; - } - - return <TileType mxEvent={this.props.mxEvent} />; - }, -}); diff --git a/src/skins/vector/views/molecules/ProgressBar.js b/src/skins/vector/views/molecules/ProgressBar.js deleted file mode 100644 index 18d1440ad8..0000000000 --- a/src/skins/vector/views/molecules/ProgressBar.js +++ /dev/null @@ -1,37 +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 ProgressBarController = require('matrix-react-sdk/lib/controllers/molecules/ProgressBar') - -module.exports = React.createClass({ - displayName: 'ProgressBar', - mixins: [ProgressBarController], - - render: function() { - // Would use an HTML5 progress tag but if that doesn't animate if you - // use the HTML attributes rather than styles - var progressStyle = { - width: ((this.props.value / this.props.max) * 100)+"%" - }; - return ( - <div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/RoomCreate.js b/src/skins/vector/views/molecules/RoomCreate.js deleted file mode 100644 index d66e014d8b..0000000000 --- a/src/skins/vector/views/molecules/RoomCreate.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -module.exports = React.createClass({ - displayName: 'RoomCreate', - - render: function() { - return ( - <div className="mx_RoomCreate"> - <div className="mx_RoomCreate_table"> - <div className="mx_RoomTile"> - <div className="mx_RoomTile_avatar"> - <img src="img/create.png" width="32" height="32"/> - </div> - <div className="mx_RoomTile_name">Create new room</div> - </div> - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/RoomHeader.js b/src/skins/vector/views/molecules/RoomHeader.js deleted file mode 100644 index d3f9119aef..0000000000 --- a/src/skins/vector/views/molecules/RoomHeader.js +++ /dev/null @@ -1,177 +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 dis = require('matrix-react-sdk/lib/dispatcher') - -var CallHandler = require('matrix-react-sdk/lib/CallHandler'); -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var RoomHeaderController = require('matrix-react-sdk/lib/controllers/molecules/RoomHeader') - -module.exports = React.createClass({ - displayName: 'RoomHeader', - mixins: [RoomHeaderController], - - onNameChange: function(new_name) { - if (this.props.room.name != new_name && new_name) { - MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name); - } - }, - - getRoomName: function() { - return this.refs.name_edit.getDOMNode().value; - }, - - onFullscreenClick: function() { - dis.dispatch({action: 'video_fullscreen', fullscreen: true}, true); - }, - - render: function() { - var EditableText = sdk.getComponent("atoms.EditableText"); - var RoomAvatar = sdk.getComponent('atoms.RoomAvatar'); - - var header; - if (this.props.simpleHeader) { - header = - <div className="mx_RoomHeader_wrapper"> - <div className="mx_RoomHeader_simpleHeader"> - { this.props.simpleHeader } - </div> - </div> - } - else { - var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - - var call_buttons; - var zoom_button; - if (this.state && this.state.call_state != 'ended') { - //var muteVideoButton; - var activeCall = ( - CallHandler.getCallForRoom(this.props.room.roomId) - ); -/* - if (activeCall && activeCall.type === "video") { - muteVideoButton = ( - <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton" - onClick={this.onMuteVideoClick}> - { - (activeCall.isLocalVideoMuted() ? - "Unmute" : "Mute") + " video" - } - </div> - ); - } - {muteVideoButton} - <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton" - onClick={this.onMuteAudioClick}> - { - (activeCall && activeCall.isMicrophoneMuted() ? - "Unmute" : "Mute") + " audio" - } - </div> -*/ - - call_buttons = ( - <div className="mx_RoomHeader_textButton" - onClick={this.onHangupClick}> - End call - </div> - ); - } - - var name = null; - var topic_el = null; - var cancel_button = null; - var save_button = null; - var settings_button = null; - var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); - if (actual_name) actual_name = actual_name.getContent().name; - if (this.props.editing) { - name = - <div className="mx_RoomHeader_nameEditing"> - <input className="mx_RoomHeader_nameInput" type="text" defaultValue={actual_name} placeholder="Name" ref="name_edit"/> - </div> - // if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div> - cancel_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onCancelClick}>Cancel</div> - save_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save Changes</div> - } else { - name = - <div className="mx_RoomHeader_name"> - <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} /> - </div> - if (topic) topic_el = <div className="mx_RoomHeader_topic" title={topic.getContent().topic}>{ topic.getContent().topic }</div>; - settings_button = ( - <div className="mx_RoomHeader_button" onClick={this.props.onSettingsClick}> - <img src="img/settings.png" width="32" height="32"/> - </div> - ); - } - - var roomAvatar = null; - if (this.props.room) { - roomAvatar = ( - <RoomAvatar room={this.props.room} width="48" height="48" /> - ); - } - - if (activeCall && activeCall.type == "video") { - zoom_button = ( - <div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}> - <img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '3px' }}/> - </div> - ); - } - - header = - <div className="mx_RoomHeader_wrapper"> - <div className="mx_RoomHeader_leftRow"> - <div className="mx_RoomHeader_avatar"> - { roomAvatar } - </div> - <div className="mx_RoomHeader_info"> - { name } - { topic_el } - </div> - </div> - {call_buttons} - {cancel_button} - {save_button} - <div className="mx_RoomHeader_rightRow"> - { settings_button } - { zoom_button } - <div className="mx_RoomHeader_button mx_RoomHeader_search"> - <img src="img/search.png" title="Search" alt="Search" width="32" height="32"/> - </div> - <div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}> - <img src="img/video.png" title="Video call" alt="Video call" width="32" height="32"/> - </div> - <div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}> - <img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32"/> - </div> - </div> - </div> - } - - return ( - <div className="mx_RoomHeader"> - { header } - </div> - ); - }, -}); diff --git a/src/skins/vector/views/molecules/RoomSettings.js b/src/skins/vector/views/molecules/RoomSettings.js deleted file mode 100644 index c5e08ff966..0000000000 --- a/src/skins/vector/views/molecules/RoomSettings.js +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); -var sdk = require('matrix-react-sdk'); - -var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings') - -module.exports = React.createClass({ - displayName: 'RoomSettings', - mixins: [RoomSettingsController], - - getTopic: function() { - return this.refs.topic.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 = <div> - <h3>Room Icon</h3> - <ChangeAvatar room={this.props.room} /> - </div>; - } - - var banned = this.props.room.getMembersWithMembership("ban"); - - return ( - <div className="mx_RoomSettings"> - <textarea className="mx_RoomSettings_description" placeholder="Topic" defaultValue={topic} ref="topic"/> <br/> - <label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/> - <label><input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users</label> <br/> - <label className="mx_RoomSettings_encrypt"><input type="checkbox" /> Encrypt room</label> <br/> - - <h3>Power levels</h3> - <div className="mx_RoomSettings_power_levels mx_RoomSettings_settings"> - <div> - <label htmlFor="mx_RoomSettings_ban_level">Ban level</label> - <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level" - disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_kick_level">Kick level</label> - <input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level" - disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_redact_level">Redact level</label> - <input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level" - disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_invite_level">Invite level</label> - <input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level" - disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_event_level">Send event level</label> - <input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level" - disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_state_level">Set state level</label> - <input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level" - disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/> - </div> - <div> - <label htmlFor="mx_RoomSettings_user_level">Default user level</label> - <input type="text" defaultValue={default_user_level} size="3" ref="users_default" - id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level} - onChange={this.onPowerLevelsChanged}/> - </div> - </div> - - <h3>User levels</h3> - <div className="mx_RoomSettings_user_levels mx_RoomSettings_settings"> - {Object.keys(user_levels).map(function(user, i) { - return ( - <div key={user}> - <label htmlFor={"mx_RoomSettings_user_"+i}>{user}</label> - <input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/> - </div> - ); - })} - </div> - - <h3>Event levels</h3> - <div className="mx_RoomSettings_event_lvels mx_RoomSettings_settings"> - {Object.keys(events_levels).map(function(event_type, i) { - return ( - <div key={event_type}> - <label htmlFor={"mx_RoomSettings_event_"+i}>{event_type}</label> - <input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/> - </div> - ); - })} - </div> - - <h3>Banned users</h3> - <div className="mx_RoomSettings_banned"> - {banned.map(function(member, i) { - return ( - <div key={i}> - {member.userId} - </div> - ); - })} - </div> - {change_avatar} - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/RoomTile.js b/src/skins/vector/views/molecules/RoomTile.js deleted file mode 100644 index 39e7b43c5b..0000000000 --- a/src/skins/vector/views/molecules/RoomTile.js +++ /dev/null @@ -1,94 +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 RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile') - -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -var sdk = require('matrix-react-sdk') - -module.exports = React.createClass({ - displayName: 'RoomTile', - mixins: [RoomTileController], - - getInitialState: function() { - return( { hover : false }); - }, - - onMouseEnter: function() { - this.setState( { hover : true }); - }, - - onMouseLeave: function() { - this.setState( { hover : false }); - }, - - render: function() { - var myUserId = MatrixClientPeg.get().credentials.userId; - var classes = classNames({ - 'mx_RoomTile': true, - 'mx_RoomTile_selected': this.props.selected, - 'mx_RoomTile_unread': this.props.unread, - 'mx_RoomTile_highlight': this.props.highlight, - 'mx_RoomTile_invited': this.props.room.currentState.members[myUserId].membership == 'invite' - }); - var name = this.props.room.name.replace(":", ":\u200b"); - var badge; - if (this.props.highlight) { - badge = <div className="mx_RoomTile_badge"/>; - } - /* - if (this.props.highlight) { - badge = <div className="mx_RoomTile_badge">!</div>; - } - else if (this.props.unread) { - badge = <div className="mx_RoomTile_badge">1</div>; - } - var nameCell; - if (badge) { - nameCell = <div className="mx_RoomTile_nameBadge"><div className="mx_RoomTile_name">{name}</div><div className="mx_RoomTile_badgeCell">{badge}</div></div>; - } - else { - nameCell = <div className="mx_RoomTile_name">{name}</div>; - } - */ - - var label; - if (!this.props.collapsed) { - label = <div className="mx_RoomTile_name">{name}</div>; - } - else if (this.state.hover) { - var RoomTooltip = sdk.getComponent("molecules.RoomTooltip"); - label = <RoomTooltip room={this.props.room}/>; - } - - var RoomAvatar = sdk.getComponent('atoms.RoomAvatar'); - return ( - <div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> - <div className="mx_RoomTile_avatar"> - <RoomAvatar room={this.props.room} /> - { badge } - </div> - { label } - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/SenderProfile.js b/src/skins/vector/views/molecules/SenderProfile.js deleted file mode 100644 index c09685aad8..0000000000 --- a/src/skins/vector/views/molecules/SenderProfile.js +++ /dev/null @@ -1,50 +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"); - -// 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: 'SenderProfile', - - render: function() { - var mxEvent = this.props.mxEvent; - var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); - - var classes = classNames({ - mx_SenderProfile: true, - // taken from https://en.wikipedia.org/wiki/Combining_character - mx_SenderProfile_zalgo: zalgo.test(name), - }); - - var msgtype = mxEvent.getContent().msgtype; - if (msgtype && msgtype == 'm.emote') { - name = ''; // emote message must include the name so don't duplicate it - } - return ( - <span className={classes}> - {name} { this.props.aux } - </span> - ); - }, -}); - diff --git a/src/skins/vector/views/molecules/ServerConfig.js b/src/skins/vector/views/molecules/ServerConfig.js deleted file mode 100644 index e48487aee8..0000000000 --- a/src/skins/vector/views/molecules/ServerConfig.js +++ /dev/null @@ -1,50 +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 Modal = require('matrix-react-sdk/lib/Modal'); -var sdk = require('matrix-react-sdk') - -var ServerConfigController = require('matrix-react-sdk/lib/controllers/molecules/ServerConfig') - -module.exports = React.createClass({ - displayName: 'ServerConfig', - mixins: [ServerConfigController], - - showHelpPopup: function() { - var ErrorDialog = sdk.getComponent('organisms.ErrorDialog'); - Modal.createDialog(ErrorDialog, { - title: 'Custom Server Options', - description: "You can use the custom server options to log into other Matrix servers by specifying a different Home server URL. This allows you to use Vector with an existing Matrix account on a different Home server. You can also set a cutom Identity server but this will affect people ability to find you if you use a server in a group other than tha main Matrix.org group.", - button: "Dismiss", - focus: true - }); - }, - - render: function() { - return ( - <div className="mx_ServerConfig"> - <label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label> - <input className="mx_Login_field" id="hsurl" type="text" value={this.state.hs_url} onChange={this.hsChanged} /> - <label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label> - <input className="mx_Login_field" type="text" value={this.state.is_url} onChange={this.isChanged} /> - <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a> - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/UserSelector.js b/src/skins/vector/views/molecules/UserSelector.js deleted file mode 100644 index 6b2336901f..0000000000 --- a/src/skins/vector/views/molecules/UserSelector.js +++ /dev/null @@ -1,46 +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 UserSelectorController = require('matrix-react-sdk/lib/controllers/molecules/UserSelector') - -module.exports = React.createClass({ - displayName: 'UserSelector', - mixins: [UserSelectorController], - - onAddUserId: function() { - this.addUser(this.refs.user_id_input.getDOMNode().value); - this.refs.user_id_input.getDOMNode().value = ""; - }, - - render: function() { - var self = this; - return ( - <div> - <ul className="mx_UserSelector_UserIdList" ref="list"> - {this.props.selected_users.map(function(user_id, i) { - return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li> - })} - </ul> - <input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/> - <button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">Add User</button> - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/voip/CallView.js b/src/skins/vector/views/molecules/voip/CallView.js deleted file mode 100644 index 07987bd379..0000000000 --- a/src/skins/vector/views/molecules/voip/CallView.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 sdk = require('matrix-react-sdk') -var CallViewController = require( - "../../../../../controllers/molecules/voip/CallView" -); - -module.exports = React.createClass({ - displayName: 'CallView', - mixins: [CallViewController], - - getVideoView: function() { - return this.refs.video; - }, - - render: function(){ - var VideoView = sdk.getComponent('molecules.voip.VideoView'); - return ( - <VideoView ref="video"/> - ); - } -}); diff --git a/src/skins/vector/views/molecules/voip/IncomingCallBox.js b/src/skins/vector/views/molecules/voip/IncomingCallBox.js deleted file mode 100644 index c3bcd82584..0000000000 --- a/src/skins/vector/views/molecules/voip/IncomingCallBox.js +++ /dev/null @@ -1,74 +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 IncomingCallBoxController = require( - "matrix-react-sdk/lib/controllers/molecules/voip/IncomingCallBox" -); - -module.exports = React.createClass({ - displayName: 'IncomingCallBox', - mixins: [IncomingCallBoxController], - - getRingAudio: function() { - return this.refs.ringAudio.getDOMNode(); - }, - - render: function() { - - // NB: This block MUST have a "key" so React doesn't clobber the elements - // between in-call / not-in-call. - var audioBlock = ( - <audio ref="ringAudio" key="voip_ring_audio" loop> - <source src="media/ring.ogg" type="audio/ogg" /> - <source src="media/ring.mp3" type="audio/mpeg" /> - </audio> - ); - - if (!this.state.incomingCall || !this.state.incomingCall.roomId) { - return ( - <div> - {audioBlock} - </div> - ); - } - var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name; - return ( - <div className="mx_IncomingCallBox"> - {audioBlock} - <img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" /> - <div className="mx_IncomingCallBox_title"> - Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller } - </div> - <div className="mx_IncomingCallBox_buttons"> - <div className="mx_IncomingCallBox_buttons_cell"> - <div className="mx_IncomingCallBox_buttons_decline" onClick={this.onRejectClick}> - Decline - </div> - </div> - <div className="mx_IncomingCallBox_buttons_cell"> - <div className="mx_IncomingCallBox_buttons_accept" onClick={this.onAnswerClick}> - Accept - </div> - </div> - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/molecules/voip/VideoView.js b/src/skins/vector/views/molecules/voip/VideoView.js deleted file mode 100644 index 4e0fb913e6..0000000000 --- a/src/skins/vector/views/molecules/voip/VideoView.js +++ /dev/null @@ -1,92 +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 dis = require('matrix-react-sdk/lib/dispatcher') - -module.exports = React.createClass({ - displayName: 'VideoView', - - componentWillMount: function() { - dis.register(this.onAction); - }, - - getRemoteVideoElement: function() { - return this.refs.remote.getDOMNode(); - }, - - getRemoteAudioElement: function() { - return this.refs.remoteAudio.getDOMNode(); - }, - - getLocalVideoElement: function() { - return this.refs.local.getDOMNode(); - }, - - setContainer: function(c) { - this.container = c; - }, - - onAction: function(payload) { - switch (payload.action) { - case 'video_fullscreen': - if (!this.container) { - return; - } - var element = this.container.getDOMNode(); - if (payload.fullscreen) { - var requestMethod = ( - element.requestFullScreen || - element.webkitRequestFullScreen || - element.mozRequestFullScreen || - element.msRequestFullscreen - ); - requestMethod.call(element); - } - else { - var exitMethod = ( - document.exitFullscreen || - document.mozCancelFullScreen || - document.webkitExitFullscreen || - document.msExitFullscreen - ); - if (exitMethod) { - exitMethod.call(document); - } - } - break; - } - }, - - render: function() { - var VideoFeed = sdk.getComponent('atoms.voip.VideoFeed'); - return ( - <div className="mx_VideoView" ref={this.setContainer}> - <div className="mx_VideoView_remoteVideoFeed"> - <VideoFeed ref="remote"/> - <audio ref="remoteAudio"/> - </div> - <div className="mx_VideoView_localVideoFeed"> - <VideoFeed ref="local"/> - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/organisms/CasLogin.js b/src/skins/vector/views/organisms/CasLogin.js deleted file mode 100644 index ad9dbed955..0000000000 --- a/src/skins/vector/views/organisms/CasLogin.js +++ /dev/null @@ -1,35 +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 CasLoginController = require('matrix-react-sdk/lib/controllers/organisms/CasLogin'); - -module.exports = React.createClass({ - displayName: 'CasLogin', - mixins: [CasLoginController], - - render: function() { - return ( - <div> - <button onClick={this.onCasClicked}>Sign in with CAS</button> - </div> - ); - }, - -}); diff --git a/src/skins/vector/views/organisms/CreateRoom.js b/src/skins/vector/views/organisms/CreateRoom.js deleted file mode 100644 index 64f5b861b3..0000000000 --- a/src/skins/vector/views/organisms/CreateRoom.js +++ /dev/null @@ -1,171 +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 CreateRoomController = require('matrix-react-sdk/lib/controllers/organisms/CreateRoom') - -var sdk = require('matrix-react-sdk') - -var PresetValues = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets').Presets; - -var Loader = require("react-loader"); - - -module.exports = React.createClass({ - displayName: 'CreateRoom', - mixins: [CreateRoomController], - - getPreset: function() { - return this.refs.presets.getPreset(); - }, - - getName: function() { - return this.refs.name_textbox.getName(); - }, - - getTopic: function() { - return this.refs.topic.getTopic(); - }, - - getAliasLocalpart: function() { - return this.refs.alias.getAliasLocalpart(); - }, - - getInvitedUsers: function() { - return this.refs.user_selector.getUserIds(); - }, - - onPresetChanged: function(preset) { - switch (preset) { - case PresetValues.PrivateChat: - this.setState({ - preset: preset, - is_private: true, - share_history: false, - }); - break; - case PresetValues.PublicChat: - this.setState({ - preset: preset, - is_private: false, - share_history: true, - }); - break; - case PresetValues.Custom: - this.setState({ - preset: preset, - }); - break; - } - }, - - onPrivateChanged: function(ev) { - this.setState({ - preset: PresetValues.Custom, - is_private: ev.target.checked, - }); - }, - - onShareHistoryChanged: function(ev) { - this.setState({ - preset: PresetValues.Custom, - share_history: ev.target.checked, - }); - }, - - onTopicChange: function(ev) { - this.setState({ - topic: ev.target.value, - }); - }, - - onNameChange: function(ev) { - this.setState({ - room_name: ev.target.value, - }); - }, - - onInviteChanged: function(invited_users) { - this.setState({ - invited_users: invited_users, - }); - }, - - onAliasChanged: function(alias) { - this.setState({ - alias: alias - }) - }, - - onEncryptChanged: function(ev) { - this.setState({ - encrypt: ev.target.checked, - }); - }, - - render: function() { - var curr_phase = this.state.phase; - if (curr_phase == this.phases.CREATING) { - return ( - <Loader/> - ); - } else { - var error_box = ""; - if (curr_phase == this.phases.ERROR) { - error_box = ( - <div className="mx_Error"> - An error occured: {this.state.error_string} - </div> - ); - } - - var CreateRoomButton = sdk.getComponent("atoms.create_room.CreateRoomButton"); - var RoomAlias = sdk.getComponent("atoms.create_room.RoomAlias"); - var Presets = sdk.getComponent("atoms.create_room.Presets"); - var UserSelector = sdk.getComponent("molecules.UserSelector"); - var RoomHeader = sdk.getComponent("molecules.RoomHeader"); - - return ( - <div className="mx_CreateRoom"> - <RoomHeader simpleHeader="Create room" /> - <div className="mx_CreateRoom_body"> - <input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br /> - <textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Topic"/> <br /> - <RoomAlias ref="alias" alias={this.state.alias} onChange={this.onAliasChanged}/> <br /> - <UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br /> - <Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br /> - <div> - <label><input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/> Make this room private</label> - </div> - <div> - <label><input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/> Share message history with new users</label> - </div> - <div className="mx_CreateRoom_encrypt"> - <label><input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/> Encrypt room</label> - </div> - <div> - <CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br /> - </div> - {error_box} - </div> - </div> - ); - } - } -}); diff --git a/src/skins/vector/views/organisms/ErrorDialog.js b/src/skins/vector/views/organisms/ErrorDialog.js deleted file mode 100644 index 992ea05054..0000000000 --- a/src/skins/vector/views/organisms/ErrorDialog.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -/* - * Usage: - * Modal.createDialog(ErrorDialog, { - * title: "some text", (default: "Error") - * description: "some more text", - * button: "Button Text", - * onClose: someFunction, - * focus: true|false (default: true) - * }); - */ - -var React = require('react'); -var ErrorDialogController = require('matrix-react-sdk/lib/controllers/organisms/ErrorDialog') - -module.exports = React.createClass({ - displayName: 'ErrorDialog', - mixins: [ErrorDialogController], - - render: function() { - return ( - <div className="mx_ErrorDialog"> - <div className="mx_ErrorDialogTitle"> - {this.props.title} - </div> - <div className="mx_Dialog_content"> - {this.props.description} - </div> - <div className="mx_Dialog_buttons"> - <button onClick={this.props.onFinished} autoFocus={this.props.focus}> - {this.props.button} - </button> - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/organisms/LeftPanel.js b/src/skins/vector/views/organisms/LeftPanel.js deleted file mode 100644 index a57f7a0a58..0000000000 --- a/src/skins/vector/views/organisms/LeftPanel.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 dis = require('matrix-react-sdk/lib/dispatcher'); - -module.exports = React.createClass({ - displayName: 'LeftPanel', - - onHideClick: function() { - dis.dispatch({ - action: 'hide_left_panel', - }); - }, - - render: function() { - var RoomList = sdk.getComponent('organisms.RoomList'); - var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu'); - var IncomingCallBox = sdk.getComponent('molecules.voip.IncomingCallBox'); - - var collapseButton; - var classes = "mx_LeftPanel"; - if (this.props.collapsed) { - classes += " collapsed"; - } - else { - collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/> - } - - return ( - <aside className={classes}> - { collapseButton } - <IncomingCallBox /> - <RoomList selectedRoom={this.props.selectedRoom} collapsed={this.props.collapsed}/> - <BottomLeftMenu collapsed={this.props.collapsed}/> - </aside> - ); - } -}); - diff --git a/src/skins/vector/views/organisms/LogoutPrompt.js b/src/skins/vector/views/organisms/LogoutPrompt.js deleted file mode 100644 index 6e347a4ef7..0000000000 --- a/src/skins/vector/views/organisms/LogoutPrompt.js +++ /dev/null @@ -1,41 +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 LogoutPromptController = require('matrix-react-sdk/lib/controllers/organisms/LogoutPrompt') - -module.exports = React.createClass({ - displayName: 'LogoutPrompt', - mixins: [LogoutPromptController], - - render: function() { - return ( - <div> - <div className="mx_Dialog_content"> - Sign out? - </div> - <div className="mx_Dialog_buttons"> - <button onClick={this.logOut}>Sign Out</button> - <button onClick={this.cancelPrompt}>Cancel</button> - </div> - </div> - ); - }, -}); - diff --git a/src/skins/vector/views/organisms/MemberList.js b/src/skins/vector/views/organisms/MemberList.js deleted file mode 100644 index 08f4e45c95..0000000000 --- a/src/skins/vector/views/organisms/MemberList.js +++ /dev/null @@ -1,147 +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 Loader = require('react-loader'); - -var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList') - -var sdk = require('matrix-react-sdk') - - -module.exports = React.createClass({ - displayName: 'MemberList', - mixins: [MemberListController], - - getInitialState: function() { - return { editing: false }; - }, - - memberSort: function(userIdA, userIdB) { - var userA = this.memberDict[userIdA].user; - var userB = this.memberDict[userIdB].user; - - var presenceMap = { - online: 3, - unavailable: 2, - offline: 1 - }; - - var presenceOrdA = userA ? presenceMap[userA.presence] : 0; - var presenceOrdB = userB ? presenceMap[userB.presence] : 0; - - if (presenceOrdA != presenceOrdB) { - return presenceOrdB - presenceOrdA; - } - - var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0; - var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0; - - return latB - latA; - }, - - makeMemberTiles: function(membership) { - var MemberTile = sdk.getComponent("molecules.MemberTile"); - - var self = this; - return self.state.members.filter(function(userId) { - var m = self.memberDict[userId]; - return m.membership == membership; - }).map(function(userId) { - var m = self.memberDict[userId]; - return ( - <MemberTile key={userId} member={m} ref={userId} /> - ); - }); - }, - - onPopulateInvite: function(inputText, shouldSubmit) { - // reset back to placeholder - this.refs.invite.setValue("Invite", false, true); - this.setState({ editing: false }); - if (!shouldSubmit) { - return; // enter key wasn't pressed - } - this.onInvite(inputText); - }, - - onClickInvite: function(ev) { - this.setState({ editing: true }); - this.refs.invite.onClickDiv(); - ev.stopPropagation(); - ev.preventDefault(); - }, - - inviteTile: function() { - var classes = classNames({ - mx_MemberTile: true, - mx_MemberTile_inviteTile: true, - mx_MemberTile_inviteEditing: this.state.editing, - }); - - var EditableText = sdk.getComponent("atoms.EditableText"); - if (this.state.inviting) { - return ( - <Loader /> - ); - } else { - return ( - <div className={ classes } onClick={ this.onClickInvite } > - <div className="mx_MemberTile_avatar"><img src="img/create-big.png" width="40" height="40" alt=""/></div> - <div className="mx_MemberTile_name"> - <EditableText ref="invite" label="Invite" placeHolder="@user:domain.com" initialValue="" onValueChanged={this.onPopulateInvite}/> - </div> - </div> - ); - } - }, - - render: function() { - var invitedSection = null; - var invitedMemberTiles = this.makeMemberTiles('invite'); - if (invitedMemberTiles.length > 0) { - invitedSection = ( - <div> - <h2>Invited</h2> - <div className="mx_MemberList_wrapper"> - {invitedMemberTiles} - </div> - </div> - ); - } - return ( - <div className="mx_MemberList"> - <div className="mx_MemberList_chevron"> - <img src="img/chevron.png" width="24" height="13"/> - </div> - <div className="mx_MemberList_border"> - <div> - <h2>Members</h2> - <div className="mx_MemberList_wrapper"> - {this.makeMemberTiles('join')} - </div> - </div> - {invitedSection} - {this.inviteTile()} - </div> - </div> - ); - } -}); - diff --git a/src/skins/vector/views/organisms/Notifier.js b/src/skins/vector/views/organisms/Notifier.js deleted file mode 100644 index b214b4cd16..0000000000 --- a/src/skins/vector/views/organisms/Notifier.js +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var NotifierController = require('matrix-react-sdk/lib/controllers/organisms/Notifier') - -var TextForEvent = require('matrix-react-sdk/lib/TextForEvent'); -var extend = require('matrix-react-sdk/lib/extend'); -var dis = require('matrix-react-sdk/lib/dispatcher'); - -var Avatar = require('../../../../Avatar'); - - -var NotifierView = { - notificationMessageForEvent: function(ev) { - return TextForEvent.textForEvent(ev); - }, - - displayNotification: function(ev, room) { - if (!global.Notification || global.Notification.permission != 'granted') { - return; - } - if (global.document.hasFocus()) { - return; - } - - var msg = this.notificationMessageForEvent(ev); - if (!msg) return; - - var title; - if (!ev.sender || room.name == ev.sender.name) { - title = room.name; - // notificationMessageForEvent includes sender, - // but we already have the sender here - if (ev.getContent().body) msg = ev.getContent().body; - } else if (ev.getType() == 'm.room.member') { - // context is all in the message here, we don't need - // to display sender info - title = room.name; - } else if (ev.sender) { - title = ev.sender.name + " (" + room.name + ")"; - // notificationMessageForEvent includes sender, - // but we've just out sender in the title - if (ev.getContent().body) msg = ev.getContent().body; - } - - var avatarUrl = ev.sender ? Avatar.avatarUrlForMember( - ev.sender, 40, 40, 'crop' - ) : null; - - var notification = new global.Notification( - title, - { - "body": msg, - "icon": avatarUrl, - "tag": "vector" - } - ); - - notification.onclick = function() { - dis.dispatch({ - action: 'view_room', - room_id: room.roomId - }); - global.focus(); - }; - - /*var audioClip; - - if (audioNotification) { - audioClip = playAudio(audioNotification); - }*/ - - global.setTimeout(function() { - notification.close(); - }, 5 * 1000); - - } -}; - -var NotifierClass = function() {}; -extend(NotifierClass.prototype, NotifierController); -extend(NotifierClass.prototype, NotifierView); - -module.exports = new NotifierClass(); - diff --git a/src/skins/vector/views/organisms/QuestionDialog.js b/src/skins/vector/views/organisms/QuestionDialog.js deleted file mode 100644 index 3941b1f925..0000000000 --- a/src/skins/vector/views/organisms/QuestionDialog.js +++ /dev/null @@ -1,66 +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'; - -/* - * Usage: - * Modal.createDialog(ErrorDialog, { - * title: "some text", (default: "Error") - * description: "some more text", - * button: "Button Text", - * onClose: someFunction, - * focus: true|false (default: true) - * }); - */ - -var React = require('react'); -var QuestionDialogController = require('matrix-react-sdk/lib/controllers/organisms/QuestionDialog') - -module.exports = React.createClass({ - displayName: 'QuestionDialog', - mixins: [QuestionDialogController], - - onOk: function() { - this.props.onFinished(true); - }, - - onCancel: function() { - this.props.onFinished(false); - }, - - render: function() { - return ( - <div className="mx_QuestionDialog"> - <div className="mx_QuestionDialogTitle"> - {this.props.title} - </div> - <div className="mx_Dialog_content"> - {this.props.description} - </div> - <div className="mx_Dialog_buttons"> - <button onClick={this.onOk} autoFocus={this.props.focus}> - {this.props.button} - </button> - - <button onClick={this.onCancel}> - Cancel - </button> - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/organisms/RightPanel.js b/src/skins/vector/views/organisms/RightPanel.js deleted file mode 100644 index 6d9dc743d0..0000000000 --- a/src/skins/vector/views/organisms/RightPanel.js +++ /dev/null @@ -1,87 +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 dis = require('matrix-react-sdk/lib/dispatcher'); - -module.exports = React.createClass({ - displayName: 'RightPanel', - - Phase : { - MemberList: 'MemberList', - FileList: 'FileList', - }, - - 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', - }); - } - }, - - render: function() { - var MemberList = sdk.getComponent('organisms.MemberList'); - var buttonGroup; - var panel; - - if (this.props.roomId) { - buttonGroup = - <div className="mx_RightPanel_headerButtonGroup"> - <div className="mx_RightPanel_headerButton mx_RightPanel_filebutton"> - <img src="img/file.png" width="32" height="32" title="Files" alt="Files"/> - </div> - <div className="mx_RightPanel_headerButton" onClick={ this.onMemberListButtonClick }> - <img src="img/members.png" width="32" height="32" title="Members" alt="Members"/> - </div> - </div>; - - if (!this.props.collapsed && this.state.phase == this.Phase.MemberList) { - panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} /> - } - } - - var classes = "mx_RightPanel"; - if (this.props.collapsed) { - classes += " collapsed"; - } - - return ( - <aside className={classes}> - <div className="mx_RightPanel_header"> - { buttonGroup } - </div> - { panel } - </aside> - ); - } -}); - diff --git a/src/skins/vector/views/organisms/RoomDirectory.js b/src/skins/vector/views/organisms/RoomDirectory.js deleted file mode 100644 index 5f5717c8e3..0000000000 --- a/src/skins/vector/views/organisms/RoomDirectory.js +++ /dev/null @@ -1,147 +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 Modal = require('matrix-react-sdk/lib/Modal'); -var sdk = require('matrix-react-sdk') -var dis = require('matrix-react-sdk/lib/dispatcher'); - -var Loader = require("react-loader"); - -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("organisms.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to get public room list", - description: err.message - }); - } - else { - self.setState({ - publicRooms: data.chunk, - loading: false, - }); - self.forceUpdate(); - } - }); - }, - - joinRoom: function(roomId) { - var self = this; - self.setState({ loading: true }); - // XXX: check that JS SDK suppresses duplicate attempts to join the same room - MatrixClientPeg.get().joinRoom(roomId).done(function() { - dis.dispatch({ - action: 'view_room', - room_id: roomId - }); - }, function(err) { - console.error("Failed to join room: %s", JSON.stringify(err)); - Modal.createDialog(ErrorDialog, { - title: "Failed to join room", - description: err.message - }); - }); - }, - - getRows: function(filter) { - 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].search(filter) >= 0 && a.num_joined_members > 0); - }).sort(function(a,b) { - return a.num_joined_members - b.num_joined_members; - }); - var rows = []; - var self = this; - for (var i = 0; i < rooms.length; i++) { - var name = rooms[i].name || rooms[i].aliases[0]; - // <img src={ MatrixClientPeg.get().getAvatarUrlForRoom(rooms[i].room_id, 40, 40, "crop") } width="40" height="40" alt=""/> - rows.unshift( - <tbody key={ rooms[i].room_id }> - <tr onClick={self.joinRoom.bind(null, rooms[i].room_id)}> - <td className="mx_RoomDirectory_name">{ name }</td> - <td>{ rooms[i].aliases[0] }</td> - <td>{ rooms[i].num_joined_members }</td> - </tr> - <tr> - <td className="mx_RoomDirectory_topic" colSpan="3">{ rooms[i].topic }</td> - </tr> - </tbody> - ); - } - return rows; - }, - - onKeyUp: function(ev) { - this.forceUpdate(); - this.setState({ roomAlias : this.refs.roomAlias.getDOMNode().value }) - if (ev.key == "Enter") { - this.joinRoom(this.refs.roomAlias.getDOMNode().value); - } - if (ev.key == "Down") { - - } - }, - - render: function() { - if (this.state.loading) { - return ( - <div className="mx_RoomDirectory"> - <Loader /> - </div> - ); - } - - var RoomHeader = sdk.getComponent('molecules.RoomHeader'); - return ( - <div className="mx_RoomDirectory"> - <RoomHeader simpleHeader="Public Rooms" /> - <div className="mx_RoomDirectory_list"> - <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/> - <div className="mx_RoomDirectory_tableWrapper"> - <table className="mx_RoomDirectory_table"> - <tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr> - { this.getRows(this.state.roomAlias) } - </table> - </div> - </div> - </div> - ); - } -}); - diff --git a/src/skins/vector/views/organisms/RoomList.js b/src/skins/vector/views/organisms/RoomList.js deleted file mode 100644 index dd8840e508..0000000000 --- a/src/skins/vector/views/organisms/RoomList.js +++ /dev/null @@ -1,65 +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 dis = require('matrix-react-sdk/lib/dispatcher'); - -var RoomListController = require('../../../../controllers/organisms/RoomList') - -module.exports = React.createClass({ - displayName: 'RoomList', - mixins: [RoomListController], - - onShowClick: function() { - dis.dispatch({ - action: 'show_left_panel', - }); - }, - - render: function() { - var CallView = sdk.getComponent('molecules.voip.CallView'); - var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget'); - - var callElement; - if (this.state.show_call_element) { - callElement = <CallView className="mx_MatrixChat_callView"/> - } - - var recentsLabel = this.props.collapsed ? - <img style={{cursor: 'pointer'}} onClick={ this.onShowClick } src="img/menu.png" width="27" height="20" alt=">"/> : - "Recents"; - - return ( - <div className="mx_RoomList" onScroll={this._repositionTooltip}> - {callElement} - <h2 className="mx_RoomList_favourites_label">Favourites</h2> - <RoomDropTarget text="Drop here to favourite"/> - - <h2 className="mx_RoomList_recents_label">{ recentsLabel }</h2> - <div className="mx_RoomList_recents"> - {this.makeRoomTiles()} - </div> - - <h2 className="mx_RoomList_archive_label">Archive</h2> - <RoomDropTarget text="Drop here to archive"/> - </div> - ); - } -}); - diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js deleted file mode 100644 index f62eb3c3cf..0000000000 --- a/src/skins/vector/views/organisms/RoomView.js +++ /dev/null @@ -1,249 +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 dis = require('matrix-react-sdk/lib/dispatcher'); - -var sdk = require('matrix-react-sdk') -var classNames = require("classnames"); -var filesize = require('filesize'); - -var RoomViewController = require('../../../../controllers/organisms/RoomView') - -var Loader = require("react-loader"); - - -module.exports = React.createClass({ - displayName: 'RoomView', - mixins: [RoomViewController], - - onSettingsClick: function() { - this.setState({editingRoomSettings: true}); - }, - - onSaveClick: function() { - this.setState({ - editingRoomSettings: false, - uploadingRoomSettings: true, - }); - - var new_name = this.refs.header.getRoomName(); - var new_topic = this.refs.room_settings.getTopic(); - var new_join_rule = this.refs.room_settings.getJoinRules(); - var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); - var new_power_levels = this.refs.room_settings.getPowerLevels(); - - this.uploadNewState( - new_name, - new_topic, - new_join_rule, - new_history_visibility, - new_power_levels - ); - }, - - onCancelClick: function() { - this.setState(this.getInitialState()); - }, - - onConferenceNotificationClick: function() { - dis.dispatch({ - action: 'place_call', - type: "video", - room_id: this.props.roomId - }); - }, - - getUnreadMessagesString: function() { - if (!this.state.numUnreadMessages) { - return ""; - } - return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : ""); - }, - - scrollToBottom: function() { - if (!this.refs.messageWrapper) return; - var messageWrapper = this.refs.messageWrapper.getDOMNode(); - messageWrapper.scrollTop = messageWrapper.scrollHeight; - }, - - render: function() { - var RoomHeader = sdk.getComponent('molecules.RoomHeader'); - var MessageComposer = sdk.getComponent('molecules.MessageComposer'); - var CallView = sdk.getComponent("molecules.voip.CallView"); - var RoomSettings = sdk.getComponent("molecules.RoomSettings"); - - if (!this.state.room) { - if (this.props.roomId) { - return ( - <div> - <button onClick={this.onJoinButtonClicked}>Join Room</button> - </div> - ); - } else { - return ( - <div /> - ); - } - } - - var myUserId = MatrixClientPeg.get().credentials.userId; - if (this.state.room.currentState.members[myUserId].membership == 'invite') { - if (this.state.joining) { - return ( - <div className="mx_RoomView"> - <Loader /> - </div> - ); - } else { - var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event; - // XXX: Leaving this intentionally basic for now because invites are about to change totally - var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; - return ( - <div className="mx_RoomView"> - <RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/> - <div className="mx_RoomView_invitePrompt"> - <div>{inviteEvent.user_id} has invited you to a room</div> - <br/> - <button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button> - <div className="error">{joinErrorText}</div> - </div> - </div> - ); - } - } else { - var scrollheader_classes = classNames({ - mx_RoomView_scrollheader: true, - loading: this.state.paginating - }); - - var statusBar = ( - <div /> - ); - - // for testing UI... - // this.state.upload = { - // uploadedBytes: 123493, - // totalBytes: 347534, - // fileName: "testing_fooble.jpg", - // } - - if (this.state.upload) { - var innerProgressStyle = { - width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%' - }; - var uploadedSize = filesize(this.state.upload.uploadedBytes); - var totalSize = filesize(this.state.upload.totalBytes); - if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) { - uploadedSize = uploadedSize.replace(/ .*/, ''); - } - statusBar = ( - <div className="mx_RoomView_uploadBar"> - <div className="mx_RoomView_uploadProgressOuter"> - <div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div> - </div> - <img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="40" height="40"/> - <img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="40" height="40"/> - <div className="mx_RoomView_uploadBytes"> - { uploadedSize } / { totalSize } - </div> - <div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div> - </div> - ); - } else { - var typingString = this.getWhoIsTypingString(); - var unreadMsgs = this.getUnreadMessagesString(); - // unread count trumps who is typing since the unread count is only - // set when you've scrolled up - if (unreadMsgs) { - statusBar = ( - <div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }> - <img src="img/newmessages.png" width="10" height="12" alt=""/> - {unreadMsgs} - </div> - ); - } - else if (typingString) { - statusBar = ( - <div className="mx_RoomView_typingBar"> - <img src="img/typing.png" width="40" height="40" alt=""/> - {typingString} - </div> - ); - } - } - - var roomEdit = null; - if (this.state.editingRoomSettings) { - roomEdit = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />; - } - if (this.state.uploadingRoomSettings) { - roomEdit = <Loader/>; - } - - var conferenceCallNotification = null; - if (this.state.displayConfCallNotification) { - conferenceCallNotification = ( - <div className="mx_RoomView_ongoingConfCallNotification" onClick={this.onConferenceNotificationClick}> - Ongoing conference call - </div> - ); - } - - var fileDropTarget = null; - if (this.state.draggingFile) { - fileDropTarget = <div className="mx_RoomView_fileDropTarget"> - <div className="mx_RoomView_fileDropTargetLabel"> - <img src="img/upload-big.png" width="46" height="61" alt="Drop File Here"/><br/> - Drop File Here - </div> - </div>; - } - - return ( - <div className="mx_RoomView"> - <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} - onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} /> - <div className="mx_RoomView_auxPanel"> - <CallView room={this.state.room}/> - { conferenceCallNotification } - { roomEdit } - </div> - <div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }> - <div className="mx_RoomView_messageListWrapper"> - { fileDropTarget } - <ol className="mx_RoomView_MessageList" aria-live="polite"> - <li className={scrollheader_classes}> - </li> - {this.getEventTiles()} - </ol> - </div> - </div> - <div className="mx_RoomView_statusArea"> - <div className="mx_RoomView_statusAreaBox"> - {statusBar} - </div> - </div> - <MessageComposer room={this.state.room} uploadFile={this.uploadFile} /> - </div> - ); - } - }, -}); diff --git a/src/skins/vector/views/organisms/UserSettings.js b/src/skins/vector/views/organisms/UserSettings.js deleted file mode 100644 index 6071f82edf..0000000000 --- a/src/skins/vector/views/organisms/UserSettings.js +++ /dev/null @@ -1,125 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings') - -var Loader = require("react-loader"); - -var Modal = require('matrix-react-sdk/lib/Modal'); - -module.exports = React.createClass({ - displayName: 'UserSettings', - mixins: [UserSettingsController], - - editAvatar: function() { - var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl); - var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar'); - var avatarDialog = ( - <div> - <ChangeAvatar initialAvatarUrl={url} /> - <div className="mx_Dialog_buttons"> - <button onClick={this.onAvatarDialogCancel}>Cancel</button> - </div> - </div> - ); - this.avatarDialog = Modal.createDialogWithElement(avatarDialog); - }, - - addEmail: function() { - - }, - - editDisplayName: function() { - this.refs.displayname.edit(); - }, - - changePassword: function() { - var ChangePassword = sdk.getComponent('molecules.ChangePassword'); - Modal.createDialog(ChangePassword); - }, - - onLogoutClicked: function(ev) { - var LogoutPrompt = sdk.getComponent('organisms.LogoutPrompt'); - this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel}); - }, - - onLogoutPromptCancel: function() { - this.logoutModal.closeDialog(); - }, - - onAvatarDialogCancel: function() { - this.avatarDialog.close(); - }, - - render: function() { - switch (this.state.phase) { - case this.Phases.Loading: - return <Loader /> - case this.Phases.Display: - var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName'); - var EnableNotificationsButton = sdk.getComponent('atoms.EnableNotificationsButton'); - return ( - <div className="mx_UserSettings"> - <div className="mx_UserSettings_User"> - <h1>User Settings</h1> - <hr/> - <div className="mx_UserSettings_User_Inner"> - <div className="mx_UserSettings_Avatar"> - <div className="mx_UserSettings_Avatar_Text">Profile Photo</div> - <div className="mx_UserSettings_Avatar_Edit" onClick={this.editAvatar}>Edit</div> - </div> - - <div className="mx_UserSettings_DisplayName"> - <ChangeDisplayName ref="displayname" /> - <div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div> - </div> - - <div className="mx_UserSettings_3pids"> - {this.state.threepids.map(function(val) { - return <div key={val.address}>{val.address}</div>; - })} - </div> - - <div className="mx_UserSettings_Add3pid" onClick={this.addEmail}>Add email</div> - </div> - </div> - - <div className="mx_UserSettings_Global"> - <h1>Global Settings</h1> - <hr/> - <div className="mx_UserSettings_Global_Inner"> - <div className="mx_UserSettings_ChangePassword" onClick={this.changePassword}> - Change Password - </div> - <div className="mx_UserSettings_ClientVersion"> - Version {this.state.clientVersion} - </div> - <div className="mx_UserSettings_EnableNotifications"> - <EnableNotificationsButton /> - </div> - <div className="mx_UserSettings_Logout"> - <button onClick={this.onLogoutClicked}>Sign Out</button> - </div> - </div> - </div> - </div> - ); - } - } -}); diff --git a/src/skins/vector/views/pages/MatrixChat.js b/src/skins/vector/views/pages/MatrixChat.js deleted file mode 100644 index 0cf754c23a..0000000000 --- a/src/skins/vector/views/pages/MatrixChat.js +++ /dev/null @@ -1,174 +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 MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat') - -// should be atomised -var Loader = require("react-loader"); - -var dis = require('matrix-react-sdk/lib/dispatcher'); -var Matrix = require("matrix-js-sdk"); -var ContextualMenu = require("../../../../ContextualMenu"); - -module.exports = React.createClass({ - displayName: 'MatrixChat', - mixins: [MatrixChatController], - - getInitialState: function() { - return { - width: 10000, - } - }, - - componentDidMount: function() { - window.addEventListener('resize', this.handleResize); - this.handleResize(); - }, - - componentWillUnmount: function() { - window.removeEventListener('resize', this.handleResize); - }, - - onAliasClick: function(event, alias) { - event.preventDefault(); - dis.dispatch({action: 'view_room_alias', room_alias: alias}); - }, - - onUserClick: function(event, userId) { - event.preventDefault(); - var MemberInfo = sdk.getComponent('molecules.MemberInfo'); - var member = new Matrix.RoomMember(null, userId); - ContextualMenu.createMenu(MemberInfo, { - member: member, - right: window.innerWidth - event.pageX, - top: event.pageY - }); - }, - - handleResize: function(e) { - var hideLhsThreshold = 1000; - var showLhsThreshold = 1000; - var hideRhsThreshold = 820; - var showRhsThreshold = 820; - - if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { - dis.dispatch({ action: 'hide_left_panel' }); - } - if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) { - dis.dispatch({ action: 'show_left_panel' }); - } - if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) { - dis.dispatch({ action: 'hide_right_panel' }); - } - if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) { - dis.dispatch({ action: 'show_right_panel' }); - } - - this.setState({width: window.innerWidth}); - }, - - onRoomCreated: function(room_id) { - dis.dispatch({ - action: "view_room", - room_id: room_id, - }); - }, - - render: function() { - var LeftPanel = sdk.getComponent('organisms.LeftPanel'); - var RoomView = sdk.getComponent('organisms.RoomView'); - var RightPanel = sdk.getComponent('organisms.RightPanel'); - var Login = sdk.getComponent('templates.Login'); - var UserSettings = sdk.getComponent('organisms.UserSettings'); - var Register = sdk.getComponent('templates.Register'); - var CreateRoom = sdk.getComponent('organisms.CreateRoom'); - var RoomDirectory = sdk.getComponent('organisms.RoomDirectory'); - var MatrixToolbar = sdk.getComponent('molecules.MatrixToolbar'); - var Notifier = sdk.getComponent('organisms.Notifier'); - - if (this.state.logged_in && this.state.ready) { - var page_element; - var right_panel = ""; - - switch (this.state.page_type) { - case this.PageTypes.RoomView: - page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} /> - right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} /> - break; - case this.PageTypes.UserSettings: - page_element = <UserSettings /> - right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> - break; - case this.PageTypes.CreateRoom: - page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/> - right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> - break; - case this.PageTypes.RoomDirectory: - page_element = <RoomDirectory /> - right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> - break; - } - - // TODO: Fix duplication here and do conditionals like we do above - if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) { - return ( - <div className="mx_MatrixChat_wrapper"> - <MatrixToolbar /> - <div className="mx_MatrixChat mx_MatrixChat_toolbarShowing"> - <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} /> - <main className="mx_MatrixChat_middlePanel"> - {page_element} - </main> - {right_panel} - </div> - </div> - ); - } - else { - return ( - <div className="mx_MatrixChat"> - <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} /> - <main className="mx_MatrixChat_middlePanel"> - {page_element} - </main> - {right_panel} - </div> - ); - } - } else if (this.state.logged_in) { - return ( - <Loader /> - ); - } else if (this.state.screen == 'register') { - return ( - <Register onLoggedIn={this.onLoggedIn} clientSecret={this.state.register_client_secret} - sessionId={this.state.register_session_id} idSid={this.state.register_id_sid} - hsUrl={this.state.register_hs_url} isUrl={this.state.register_is_url} - registrationUrl={this.props.registrationUrl} - /> - ); - } else { - return ( - <Login onLoggedIn={this.onLoggedIn} /> - ); - } - } -}); diff --git a/src/skins/vector/views/templates/Login.js b/src/skins/vector/views/templates/Login.js deleted file mode 100644 index 0378153a7f..0000000000 --- a/src/skins/vector/views/templates/Login.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 sdk = require('matrix-react-sdk') -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); - -var Loader = require("react-loader"); - -var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login') - -var config = require('../../../../../config.json'); - -module.exports = React.createClass({ - displayName: 'Login', - mixins: [LoginController], - - getInitialState: function() { - return { - serverConfigVisible: false - }; - }, - - componentWillMount: function() { - this.onHSChosen(); - this.customHsUrl = config.default_hs_url; - this.customIsUrl = config.default_is_url; - }, - - getHsUrl: function() { - if (this.state.serverConfigVisible) { - return this.customHsUrl; - } else { - return config.default_hs_url; - } - }, - - getIsUrl: function() { - if (this.state.serverConfigVisible) { - return this.customIsUrl; - } else { - return config.default_is_url; - } - }, - - onServerConfigVisibleChange: function(ev) { - this.setState({ - serverConfigVisible: ev.target.checked - }, this.onHsUrlChanged); - }, - - /** - * Gets the form field values for the current login stage - */ - getFormVals: function() { - return { - 'username': this.refs.user.getDOMNode().value.trim(), - 'password': this.refs.pass.getDOMNode().value.trim() - }; - }, - - onHsUrlChanged: function() { - var newHsUrl = this.refs.serverConfig.getHsUrl().trim(); - var newIsUrl = this.refs.serverConfig.getIsUrl().trim(); - - if (newHsUrl == this.customHsUrl && - newIsUrl == this.customIsUrl) - { - return; - } - else { - this.customHsUrl = newHsUrl; - this.customIsUrl = newIsUrl; - } - - MatrixClientPeg.replaceUsingUrls( - this.getHsUrl(), - this.getIsUrl() - ); - this.setState({ - hs_url: this.getHsUrl(), - is_url: this.getIsUrl() - }); - // XXX: HSes do not have to offer password auth, so we - // need to update and maybe show a different component - // when a new HS is entered. - if (this.updateHsTimeout) { - clearTimeout(this.updateHsTimeout); - } - var self = this; - this.updateHsTimeout = setTimeout(function() { - self.onHSChosen(); - }, 1000); - }, - - componentForStep: function(step) { - switch (step) { - case 'choose_hs': - case 'fetch_stages': - var serverConfigStyle = {}; - serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none'; - var ServerConfig = sdk.getComponent("molecules.ServerConfig"); - - return ( - <div> - <input className="mx_Login_checkbox" id="advanced" type="checkbox" checked={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} /> - <label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label> - <div style={serverConfigStyle}> - <ServerConfig ref="serverConfig" - defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl} - onHsUrlChanged={this.onHsUrlChanged} - /> - </div> - </div> - ); - // XXX: clearly these should be separate organisms - case 'stage_m.login.password': - return ( - <div> - <form onSubmit={this.onUserPassEntered}> - <input className="mx_Login_field" ref="user" type="text" value={this.state.username} onChange={this.onUsernameChanged} placeholder="Email or user name" /><br /> - <input className="mx_Login_field" ref="pass" type="password" value={this.state.password} onChange={this.onPasswordChanged} placeholder="Password" /><br /> - { this.componentForStep('choose_hs') } - <input className="mx_Login_submit" type="submit" value="Log in" /> - </form> - </div> - ); - case 'stage_m.login.cas': - var CasLogin = sdk.getComponent('organisms.CasLogin'); - return ( - <CasLogin /> - ); - } - }, - - onUsernameChanged: function(ev) { - this.setState({username: ev.target.value}); - }, - - onPasswordChanged: function(ev) { - this.setState({password: ev.target.value}); - }, - - loginContent: function() { - var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null; - return ( - <div> - <h2>Sign in</h2> - {this.componentForStep(this.state.step)} - <div className="mx_Login_error"> - { loader } - {this.state.errorText} - </div> - <a className="mx_Login_create" onClick={this.showRegister} href="#">Create a new account</a> - <br/> - <div className="mx_Login_links"> - <a href="https://medium.com/@Vector">blog</a> · - <a href="https://twitter.com/@VectorCo">twitter</a> · - <a href="https://github.com/vector-im/vector-web">github</a> · - <a href="https://matrix.org">powered by Matrix</a> - </div> - </div> - ); - }, - - render: function() { - return ( - <div className="mx_Login"> - <div className="mx_Login_box"> - <div className="mx_Login_logo"> - <img src="img/logo.png" width="249" height="78" alt="vector"/> - </div> - {this.loginContent()} - </div> - </div> - ); - } -}); diff --git a/src/skins/vector/views/templates/Register.js b/src/skins/vector/views/templates/Register.js deleted file mode 100644 index 24f88b05e2..0000000000 --- a/src/skins/vector/views/templates/Register.js +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; - -var React = require('react'); - -var sdk = require('matrix-react-sdk') -var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg') - -var Loader = require("react-loader"); - -var RegisterController = require('../../../../controllers/templates/Register') - -var config = require('../../../../../config.json'); - -module.exports = React.createClass({ - displayName: 'Register', - mixins: [RegisterController], - - getInitialState: function() { - return { - serverConfigVisible: false - }; - }, - - componentWillMount: function() { - this.customHsUrl = config.default_hs_url; - this.customIsUrl = config.default_is_url; - }, - - getRegFormVals: function() { - return { - email: this.refs.email.getDOMNode().value.trim(), - username: this.refs.username.getDOMNode().value.trim(), - password: this.refs.password.getDOMNode().value.trim(), - confirmPassword: this.refs.confirmPassword.getDOMNode().value.trim() - }; - }, - - getHsUrl: function() { - if (this.state.serverConfigVisible) { - return this.customHsUrl; - } else { - return config.default_hs_url; - } - }, - - getIsUrl: function() { - if (this.state.serverConfigVisible) { - return this.customIsUrl; - } else { - return config.default_is_url; - } - }, - - onServerConfigVisibleChange: function(ev) { - this.setState({ - serverConfigVisible: ev.target.checked - }); - }, - - onServerUrlChanged: function(newUrl) { - this.customHsUrl = this.refs.serverConfig.getHsUrl(); - this.customIsUrl = this.refs.serverConfig.getIsUrl(); - this.forceUpdate(); - }, - - onProfileContinueClicked: function() { - this.onAccountReady(); - }, - - componentForStep: function(step) { - switch (step) { - case 'initial': - var serverConfigStyle = {}; - serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none'; - var ServerConfig = sdk.getComponent("molecules.ServerConfig"); - return ( - <div> - <form onSubmit={this.onInitialStageSubmit}> - <input className="mx_Login_field" type="text" ref="email" placeholder="Email address" defaultValue={this.savedParams.email} /><br /> - <input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} /><br /> - <input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br /> - <input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br /> - - <input className="mx_Login_checkbox" id="advanced" type="checkbox" value={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} /> - <label htmlFor="advanced">Use custom server options (advanced)</label> - <div style={serverConfigStyle}> - <ServerConfig ref="serverConfig" - defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl} - onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} /> - </div> - <br /> - <input className="mx_Login_submit" type="submit" value="Register" /> - </form> - </div> - ); - // XXX: clearly these should be separate organisms - case 'stage_m.login.email.identity': - return ( - <div> - Please check your email to continue registration. - </div> - ); - case 'stage_m.login.recaptcha': - return ( - <div ref="recaptchaContainer"> - This Home Server would like to make sure you are not a robot - <div id="mx_recaptcha"></div> - </div> - ); - } - }, - - registerContent: function() { - if (this.state.busy) { - return ( - <Loader /> - ); - } else if (this.state.step == 'profile') { - var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName'); - var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar'); - return ( - <div className="mx_Login_profile"> - Set a display name: - <ChangeDisplayName /> - Upload an avatar: - <ChangeAvatar initialAvatarUrl={MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl)} /> - <button onClick={this.onProfileContinueClicked}>Continue</button> - </div> - ); - } else { - return ( - <div> - <h2>Create an account</h2> - {this.componentForStep(this.state.step)} - <div className="mx_Login_error">{this.state.errorText}</div> - <a className="mx_Login_create" onClick={this.showLogin} href="#">I already have an account</a> - </div> - ); - } - }, - - onBadFields: function(bad) { - var keys = Object.keys(bad); - var strings = []; - for (var i = 0; i < keys.length; ++i) { - switch (bad[keys[i]]) { - case this.FieldErrors.PasswordMismatch: - strings.push("Passwords don't match"); - break; - case this.FieldErrors.Missing: - strings.push("Missing "+keys[i]); - break; - case this.FieldErrors.TooShort: - strings.push(keys[i]+" is too short"); - break; - case this.FieldErrors.InUse: - strings.push(keys[i]+" is already taken"); - break; - case this.FieldErrors.Length: - strings.push(keys[i] + " is not long enough."); - break; - default: - console.error("Unhandled FieldError: %s", bad[keys[i]]); - break; - } - } - var errtxt = strings.join(', '); - this.setState({ - errorText: errtxt - }); - }, - - render: function() { - return ( - <div className="mx_Login"> - <div className="mx_Login_box"> - <div className="mx_Login_logo"> - <img src="img/logo.png" width="249" height="78" alt="vector"/> - </div> - {this.registerContent()} - </div> - </div> - ); - } -}); diff --git a/src/vector/index.js b/src/vector/index.js index 22db05a38f..5b5701a532 100644 --- a/src/vector/index.js +++ b/src/vector/index.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. @@ -16,15 +16,61 @@ limitations under the License. 'use strict'; +// for ES6 stuff like startsWith() that Safari doesn't handle +// and babel doesn't do by default +require('babel-polyfill'); + +// CSS requires: just putting them here for now as CSS is going to be +// refactored soon anyway +require('../../vector/components.css'); +require('gemini-scrollbar/gemini-scrollbar.css'); +require('gfm.css/gfm.css'); +require('highlight.js/styles/github.css'); + +var RunModernizrTests = require("./modernizr"); // this side-effects a global var React = require("react"); +var ReactDOM = require("react-dom"); var sdk = require("matrix-react-sdk"); -sdk.loadSkin(require('../skins/vector/skindex')); -sdk.loadModule(require('../modules/VectorConferenceHandler')); +sdk.loadSkin(require('../component-index')); +var VectorConferenceHandler = require('../VectorConferenceHandler'); +var configJson = require("../../config.json"); +var UpdateChecker = require("./updater"); var qs = require("querystring"); var lastLocationHashSet = null; +var CallHandler = require("matrix-react-sdk/lib/CallHandler"); +CallHandler.setConferenceHandler(VectorConferenceHandler); + +function checkBrowserFeatures(featureList) { + if (!window.Modernizr) { + console.error("Cannot check features - Modernizr global is missing."); + return false; + } + var featureComplete = true; + for (var i = 0; i < featureList.length; i++) { + if (window.Modernizr[featureList[i]] === undefined) { + console.error( + "Looked for feature '%s' but Modernizr has no results for this. " + + "Has it been configured correctly?", featureList[i] + ); + return false; + } + if (window.Modernizr[featureList[i]] === false) { + console.error("Browser missing feature: '%s'", featureList[i]); + // toggle flag rather than return early so we log all missing features + // rather than just the first. + featureComplete = false; + } + } + return featureComplete; +} + +var validBrowser = checkBrowserFeatures([ + "displaytable", "flexbox", "es5object", "es5function", "localstorage", + "objectfit" +]); // We want to support some name / value pairs in the fragment // so we're re-using query string like format @@ -36,16 +82,23 @@ function parseQsFromFragment(location) { return {}; } +function parseQs(location) { + return qs.parse(location.search.substring(1)); +} + // Here, we do some crude URL analysis to allow -// deep-linking. We only support registration -// deep-links in this example. +// deep-linking. function routeUrl(location) { - if (location.hash.indexOf('#/register') == 0) { + var params = parseQs(location); + var loginToken = params.loginToken; + if (loginToken) { + window.matrixChat.showScreen('token_login', parseQs(location)); + } + else if (location.hash.indexOf('#/register') == 0) { window.matrixChat.showScreen('register', parseQsFromFragment(location)); - } else if (location.hash.indexOf('#/login/cas') == 0) { - window.matrixChat.showScreen('cas_login', parseQsFromFragment(location)); } else { - window.matrixChat.showScreen(location.hash.substring(2)); + var hashparts = location.hash.split('?'); + window.matrixChat.showScreen(hashparts[0].substring(2), parseQsFromFragment(location)); } } @@ -57,6 +110,10 @@ function onHashChange(ev) { routeUrl(window.location); } +function onVersion(current, latest) { + window.matrixChat.onVersion(current, latest); +} + var loaded = false; var lastLoadedScreen = null; @@ -69,6 +126,7 @@ var onNewScreen = function(screen) { var hash = '#/' + screen; lastLocationHashSet = hash; window.location.hash = hash; + if (ga) ga('send', 'pageview', window.location.pathname + window.location.search + window.location.hash); } } @@ -84,14 +142,13 @@ var makeRegistrationUrl = function() { '#/register'; } -var MatrixChat = sdk.getComponent('pages.MatrixChat'); -window.matrixChat = React.render( - <MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />, - document.getElementById('matrixchat') -); - window.addEventListener('hashchange', onHashChange); window.onload = function() { + if (!validBrowser) { + return; + } + UpdateChecker.setVersionListener(onVersion); + UpdateChecker.run(); routeUrl(window.location); loaded = true; if (lastLoadedScreen) { @@ -100,3 +157,34 @@ window.onload = function() { } } +function loadApp() { + if (validBrowser) { + var MatrixChat = sdk.getComponent('structures.MatrixChat'); + window.matrixChat = ReactDOM.render( + <MatrixChat + onNewScreen={onNewScreen} + registrationUrl={makeRegistrationUrl()} + ConferenceHandler={VectorConferenceHandler} + config={configJson} + startingQueryParams={parseQsFromFragment(window.location)} + enableGuest={true} />, + document.getElementById('matrixchat') + ); + } + else { + console.error("Browser is missing required features."); + // take to a different landing page to AWOOOOOGA at the user + var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage"); + window.matrixChat = ReactDOM.render( + <CompatibilityPage onAccept={function() { + validBrowser = true; + console.log("User accepts the compatibility risks."); + loadApp(); + window.onload(); // still do the same code paths for compatible clients + }} />, + document.getElementById('matrixchat') + ); + } +} + +loadApp(); diff --git a/src/vector/modernizr.js b/src/vector/modernizr.js new file mode 100644 index 0000000000..5ef7778aeb --- /dev/null +++ b/src/vector/modernizr.js @@ -0,0 +1,3 @@ +/*! modernizr 3.1.0 (Custom Build) | MIT * + * http://modernizr.com/download/?-displaytable-es5-flexbox-localstorage-objectfit-cssclassprefix:modernizr_ !*/ +!function(window,document,undefined){function is(e,t){return typeof e===t}function testRunner(){var e,t,r,n,o,s,i;for(var d in tests){if(e=[],t=tests[d],t.name&&(e.push(t.name.toLowerCase()),t.options&&t.options.aliases&&t.options.aliases.length))for(r=0;r<t.options.aliases.length;r++)e.push(t.options.aliases[r].toLowerCase());for(n=is(t.fn,"function")?t.fn():t.fn,o=0;o<e.length;o++)s=e[o],i=s.split("."),1===i.length?Modernizr[i[0]]=n:(!Modernizr[i[0]]||Modernizr[i[0]]instanceof Boolean||(Modernizr[i[0]]=new Boolean(Modernizr[i[0]])),Modernizr[i[0]][i[1]]=n),classes.push((n?"":"no-")+i.join("-"))}}function setClasses(e){var t=docElement.className,r=Modernizr._config.classPrefix||"";if(isSVG&&(t=t.baseVal),Modernizr._config.enableJSClass){var n=new RegExp("(^|\\s)"+r+"no-js(\\s|$)");t=t.replace(n,"$1"+r+"js$2")}Modernizr._config.enableClasses&&(t+=" "+r+e.join(" "+r),isSVG?docElement.className.baseVal=t:docElement.className=t)}function createElement(){return"function"!=typeof document.createElement?document.createElement(arguments[0]):isSVG?document.createElementNS.call(document,"http://www.w3.org/2000/svg",arguments[0]):document.createElement.apply(document,arguments)}function getBody(){var e=document.body;return e||(e=createElement(isSVG?"svg":"body"),e.fake=!0),e}function injectElementWithStyles(e,t,r,n){var o,s,i,d,a="modernizr",l=createElement("div"),c=getBody();if(parseInt(r,10))for(;r--;)i=createElement("div"),i.id=n?n[r]:a+(r+1),l.appendChild(i);return o=createElement("style"),o.type="text/css",o.id="s"+a,(c.fake?c:l).appendChild(o),c.appendChild(l),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e)),l.id=a,c.fake&&(c.style.background="",c.style.overflow="hidden",d=docElement.style.overflow,docElement.style.overflow="hidden",docElement.appendChild(c)),s=t(l,e),c.fake?(c.parentNode.removeChild(c),docElement.style.overflow=d,docElement.offsetHeight):l.parentNode.removeChild(l),!!s}function contains(e,t){return!!~(""+e).indexOf(t)}function domToCSS(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function nativeTestProps(e,t){var r=e.length;if("CSS"in window&&"supports"in window.CSS){for(;r--;)if(window.CSS.supports(domToCSS(e[r]),t))return!0;return!1}if("CSSSupportsRule"in window){for(var n=[];r--;)n.push("("+domToCSS(e[r])+":"+t+")");return n=n.join(" or "),injectElementWithStyles("@supports ("+n+") { #modernizr { position: absolute; } }",function(e){return"absolute"==getComputedStyle(e,null).position})}return undefined}function cssToDOM(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,r){return t+r.toUpperCase()}).replace(/^-/,"")}function testProps(e,t,r,n){function o(){i&&(delete mStyle.style,delete mStyle.modElem)}if(n=is(n,"undefined")?!1:n,!is(r,"undefined")){var s=nativeTestProps(e,r);if(!is(s,"undefined"))return s}for(var i,d,a,l,c,f=["modernizr","tspan"];!mStyle.style;)i=!0,mStyle.modElem=createElement(f.shift()),mStyle.style=mStyle.modElem.style;for(a=e.length,d=0;a>d;d++)if(l=e[d],c=mStyle.style[l],contains(l,"-")&&(l=cssToDOM(l)),mStyle.style[l]!==undefined){if(n||is(r,"undefined"))return o(),"pfx"==t?l:!0;try{mStyle.style[l]=r}catch(u){}if(mStyle.style[l]!=c)return o(),"pfx"==t?l:!0}return o(),!1}function fnBind(e,t){return function(){return e.apply(t,arguments)}}function testDOMProps(e,t,r){var n;for(var o in e)if(e[o]in t)return r===!1?e[o]:(n=t[e[o]],is(n,"function")?fnBind(n,r||t):n);return!1}function testPropsAll(e,t,r,n,o){var s=e.charAt(0).toUpperCase()+e.slice(1),i=(e+" "+cssomPrefixes.join(s+" ")+s).split(" ");return is(t,"string")||is(t,"undefined")?testProps(i,t,n,o):(i=(e+" "+domPrefixes.join(s+" ")+s).split(" "),testDOMProps(i,t,r))}function testAllProps(e,t,r){return testPropsAll(e,undefined,undefined,t,r)}var tests=[],ModernizrProto={_version:"3.1.0",_config:{classPrefix:"modernizr_",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var r=this;setTimeout(function(){t(r[e])},0)},addTest:function(e,t,r){tests.push({name:e,fn:t,options:r})},addAsyncTest:function(e){tests.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=ModernizrProto,Modernizr=new Modernizr;var classes=[],docElement=document.documentElement,isSVG="svg"===docElement.nodeName.toLowerCase(),testStyles=ModernizrProto.testStyles=injectElementWithStyles;testStyles("#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}",function(e){var t,r=e.childNodes;t=r[0].offsetLeft<r[1].offsetLeft,Modernizr.addTest("displaytable",t,{aliases:["display-table"]})},2);var omPrefixes="Moz O ms Webkit",cssomPrefixes=ModernizrProto._config.usePrefixes?omPrefixes.split(" "):[];ModernizrProto._cssomPrefixes=cssomPrefixes;var modElem={elem:createElement("modernizr")};Modernizr._q.push(function(){delete modElem.elem});var mStyle={style:modElem.elem.style};Modernizr._q.unshift(function(){delete mStyle.style});var domPrefixes=ModernizrProto._config.usePrefixes?omPrefixes.toLowerCase().split(" "):[];ModernizrProto._domPrefixes=domPrefixes,ModernizrProto.testAllProps=testPropsAll,ModernizrProto.testAllProps=testAllProps,Modernizr.addTest("flexbox",testAllProps("flexBasis","1px",!0)),Modernizr.addTest("es5array",function(){return!!(Array.prototype&&Array.prototype.every&&Array.prototype.filter&&Array.prototype.forEach&&Array.prototype.indexOf&&Array.prototype.lastIndexOf&&Array.prototype.map&&Array.prototype.some&&Array.prototype.reduce&&Array.prototype.reduceRight&&Array.isArray)}),Modernizr.addTest("es5date",function(){var e="2013-04-12T06:06:37.307Z",t=!1;try{t=!!Date.parse(e)}catch(r){}return!!(Date.now&&Date.prototype&&Date.prototype.toISOString&&Date.prototype.toJSON&&t)}),Modernizr.addTest("es5function",function(){return!(!Function.prototype||!Function.prototype.bind)}),Modernizr.addTest("es5object",function(){return!!(Object.keys&&Object.create&&Object.getPrototypeOf&&Object.getOwnPropertyNames&&Object.isSealed&&Object.isFrozen&&Object.isExtensible&&Object.getOwnPropertyDescriptor&&Object.defineProperty&&Object.defineProperties&&Object.seal&&Object.freeze&&Object.preventExtensions)}),Modernizr.addTest("strictmode",function(){"use strict";return!this}()),Modernizr.addTest("es5string",function(){return!(!String.prototype||!String.prototype.trim)}),Modernizr.addTest("json","JSON"in window&&"parse"in JSON&&"stringify"in JSON),Modernizr.addTest("es5syntax",function(){var value,obj,stringAccess,getter,setter,reservedWords,zeroWidthChars;try{return stringAccess=eval('"foobar"[3] === "b"'),getter=eval("({ get x(){ return 1 } }).x === 1"),eval("({ set x(v){ value = v; } }).x = 1"),setter=1===value,eval("obj = ({ if: 1 })"),reservedWords=1===obj["if"],zeroWidthChars=eval("_ = true"),stringAccess&&getter&&setter&&reservedWords&&zeroWidthChars}catch(ignore){return!1}}),Modernizr.addTest("es5undefined",function(){var e,t;try{t=window.undefined,window.undefined=12345,e="undefined"==typeof window.undefined,window.undefined=t}catch(r){return!1}return e}),Modernizr.addTest("es5",function(){return!!(Modernizr.es5array&&Modernizr.es5date&&Modernizr.es5function&&Modernizr.es5object&&Modernizr.strictmode&&Modernizr.es5string&&Modernizr.json&&Modernizr.es5syntax&&Modernizr.es5undefined)});var atRule=function(e){var t,r=prefixes.length,n=window.CSSRule;if("undefined"==typeof n)return undefined;if(!e)return!1;if(e=e.replace(/^@/,""),t=e.replace(/-/g,"_").toUpperCase()+"_RULE",t in n)return"@"+e;for(var o=0;r>o;o++){var s=prefixes[o],i=s.toUpperCase()+"_"+t;if(i in n)return"@-"+s.toLowerCase()+"-"+e}return!1};ModernizrProto.atRule=atRule;var prefixed=ModernizrProto.prefixed=function(e,t,r){return 0===e.indexOf("@")?atRule(e):(-1!=e.indexOf("-")&&(e=cssToDOM(e)),t?testPropsAll(e,t,r):testPropsAll(e,"pfx"))};Modernizr.addTest("objectfit",!!prefixed("objectFit"),{aliases:["object-fit"]}),Modernizr.addTest("localstorage",function(){var e="modernizr";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(t){return!1}}),testRunner(),setClasses(classes),delete ModernizrProto.addTest,delete ModernizrProto.addAsyncTest;for(var i=0;i<Modernizr._q.length;i++)Modernizr._q[i]();window.Modernizr=Modernizr}(window,document); \ No newline at end of file diff --git a/src/vector/updater.js b/src/vector/updater.js new file mode 100644 index 0000000000..e8d6830d02 --- /dev/null +++ b/src/vector/updater.js @@ -0,0 +1,60 @@ +/* +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 POKE_RATE_MS = 10 * 60 * 1000; // 10 min +var currentVersion = null; +var latestVersion = null; +var listener = function(){}; // NOP + +module.exports = { + setVersionListener: function(fn) { // invoked with fn(currentVer, newVer) + listener = fn; + }, + + run: function() { + var req = new XMLHttpRequest(); + req.addEventListener("load", function() { + if (!req.responseText) { + return; + } + var ver = req.responseText.trim(); + if (!currentVersion) { + currentVersion = ver; + listener(currentVersion, currentVersion); + } + + if (ver !== latestVersion) { + latestVersion = ver; + if (module.exports.hasNewVersion()) { + console.log("Current=%s Latest=%s", currentVersion, latestVersion); + listener(currentVersion, latestVersion); + } + } + }); + var cacheBuster = "?ts=" + new Date().getTime(); + req.open("GET", "version" + cacheBuster); + req.send(); // can't suppress 404s from being logged. + + setTimeout(module.exports.run, POKE_RATE_MS); + }, + + getCurrentVersion: function() { + return currentVersion; + }, + + hasNewVersion: function() { + return currentVersion && latestVersion && (currentVersion !== latestVersion); + } +}; diff --git a/vector/icons/browserconfig.xml b/vector/icons/browserconfig.xml deleted file mode 100644 index a75bbad326..0000000000 --- a/vector/icons/browserconfig.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<browserconfig> - <msapplication> - <tile> - <square70x70logo src="/icons/mstile-70x70.png"/> - <square150x150logo src="/icons/mstile-150x150.png"/> - <square310x310logo src="/icons/mstile-310x310.png"/> - <wide310x150logo src="/icons/mstile-310x150.png"/> - <TileColor>#da532c</TileColor> - </tile> - </msapplication> -</browserconfig> diff --git a/vector/img b/vector/img index 8f1382c0ad..855fa9738f 120000 --- a/vector/img +++ b/vector/img @@ -1 +1 @@ -../src/skins/vector/img \ No newline at end of file +../src/skins/vector/img/ \ No newline at end of file diff --git a/vector/index.html b/vector/index.html index 2985994b10..d0043001ea 100644 --- a/vector/index.html +++ b/vector/index.html @@ -3,30 +3,35 @@ <head> <meta charset="utf-8"> <title>Vector</title> - <link href='fonts/Lato.css' rel='stylesheet' type='text/css'> - <link rel="apple-touch-icon" sizes="57x57" href="/icons/apple-touch-icon-57x57.png"> - <link rel="apple-touch-icon" sizes="60x60" href="/icons/apple-touch-icon-60x60.png"> - <link rel="apple-touch-icon" sizes="72x72" href="/icons/apple-touch-icon-72x72.png"> - <link rel="apple-touch-icon" sizes="76x76" href="/icons/apple-touch-icon-76x76.png"> - <link rel="apple-touch-icon" sizes="114x114" href="/icons/apple-touch-icon-114x114.png"> - <link rel="apple-touch-icon" sizes="120x120" href="/icons/apple-touch-icon-120x120.png"> - <link rel="apple-touch-icon" sizes="144x144" href="/icons/apple-touch-icon-144x144.png"> - <link rel="apple-touch-icon" sizes="152x152" href="/icons/apple-touch-icon-152x152.png"> - <link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon-180x180.png"> - <link rel="icon" type="image/png" href="/icons/favicon-32x32.png" sizes="32x32"> - <link rel="icon" type="image/png" href="/icons/android-chrome-192x192.png" sizes="192x192"> - <link rel="icon" type="image/png" href="/icons/favicon-96x96.png" sizes="96x96"> - <link rel="icon" type="image/png" href="/icons/favicon-16x16.png" sizes="16x16"> - <link rel="manifest" href="/icons/manifest.json"> - <link rel="shortcut icon" href="/icons/favicon.ico"> + <link href='fonts/OpenSans.css' rel='stylesheet' type='text/css'> + <link rel="apple-touch-icon" sizes="57x57" href="vector-icons/apple-touch-icon-57x57.png"> + <link rel="apple-touch-icon" sizes="60x60" href="vector-icons/apple-touch-icon-60x60.png"> + <link rel="apple-touch-icon" sizes="72x72" href="vector-icons/apple-touch-icon-72x72.png"> + <link rel="apple-touch-icon" sizes="76x76" href="vector-icons/apple-touch-icon-76x76.png"> + <link rel="apple-touch-icon" sizes="114x114" href="vector-icons/apple-touch-icon-114x114.png"> + <link rel="apple-touch-icon" sizes="120x120" href="vector-icons/apple-touch-icon-120x120.png"> + <link rel="apple-touch-icon" sizes="144x144" href="vector-icons/apple-touch-icon-144x144.png"> + <link rel="apple-touch-icon" sizes="152x152" href="vector-icons/apple-touch-icon-152x152.png"> + <link rel="apple-touch-icon" sizes="180x180" href="vector-icons/apple-touch-icon-180x180.png"> + <link rel="manifest" href="vector-icons/manifest.json"> + <link rel="shortcut icon" href="vector-icons/favicon.ico"> <meta name="apple-mobile-web-app-title" content="Vector"> <meta name="application-name" content="Vector"> <meta name="msapplication-TileColor" content="#da532c"> - <meta name="msapplication-TileImage" content="/icons/mstile-144x144.png"> - <meta name="msapplication-config" content="/icons/browserconfig.xml"> + <meta name="msapplication-TileImage" content="vector-icons/mstile-144x144.png"> + <meta name="msapplication-config" content="vector-icons/browserconfig.xml"> <meta name="theme-color" content="#ffffff"> </head> <body style="height: 100%;"> + <section id="matrixchat" style="height: 100%;"></section> + <script src="bundle.js"></script> + <noscript>Sorry, Vector requires JavaScript to be enabled.</noscript> + <link rel="stylesheet" href="bundle.css"> + <img src="img/warning.svg" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/> + <audio id="ringAudio" loop> + <source src="media/ring.ogg" type="audio/ogg" /> + <source src="media/ring.mp3" type="audio/mpeg" /> + </audio> <audio id="ringbackAudio" loop> <source src="media/ringback.ogg" type="audio/ogg" /> <source src="media/ringback.mp3" type="audio/mpeg" /> @@ -39,8 +44,19 @@ <source src="media/busy.ogg" type="audio/ogg" /> <source src="media/busy.mp3" type="audio/mpeg" /> </audio> - <section id="matrixchat" style="height: 100%;"></section> - <script src="bundle.js"></script> - <link rel="stylesheet" href="bundle.css"> + <script> + if (window.location.host === 'www.vector.im' || window.location.host === 'vector.im') { + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-54779209-2', 'auto'); + ga('send', 'pageview', window.location.pathname + window.location.search + window.location.hash); + } + else { + var ga = null; + } + </script> </body> </html> diff --git a/vector/icons/android-chrome-144x144.png b/vector/vector-icons/android-chrome-144x144.png similarity index 100% rename from vector/icons/android-chrome-144x144.png rename to vector/vector-icons/android-chrome-144x144.png diff --git a/vector/icons/android-chrome-192x192.png b/vector/vector-icons/android-chrome-192x192.png similarity index 100% rename from vector/icons/android-chrome-192x192.png rename to vector/vector-icons/android-chrome-192x192.png diff --git a/vector/icons/android-chrome-36x36.png b/vector/vector-icons/android-chrome-36x36.png similarity index 100% rename from vector/icons/android-chrome-36x36.png rename to vector/vector-icons/android-chrome-36x36.png diff --git a/vector/icons/android-chrome-48x48.png b/vector/vector-icons/android-chrome-48x48.png similarity index 100% rename from vector/icons/android-chrome-48x48.png rename to vector/vector-icons/android-chrome-48x48.png diff --git a/vector/icons/android-chrome-72x72.png b/vector/vector-icons/android-chrome-72x72.png similarity index 100% rename from vector/icons/android-chrome-72x72.png rename to vector/vector-icons/android-chrome-72x72.png diff --git a/vector/icons/android-chrome-96x96.png b/vector/vector-icons/android-chrome-96x96.png similarity index 100% rename from vector/icons/android-chrome-96x96.png rename to vector/vector-icons/android-chrome-96x96.png diff --git a/vector/icons/apple-touch-icon-114x114.png b/vector/vector-icons/apple-touch-icon-114x114.png similarity index 100% rename from vector/icons/apple-touch-icon-114x114.png rename to vector/vector-icons/apple-touch-icon-114x114.png diff --git a/vector/icons/apple-touch-icon-120x120.png b/vector/vector-icons/apple-touch-icon-120x120.png similarity index 100% rename from vector/icons/apple-touch-icon-120x120.png rename to vector/vector-icons/apple-touch-icon-120x120.png diff --git a/vector/icons/apple-touch-icon-144x144.png b/vector/vector-icons/apple-touch-icon-144x144.png similarity index 100% rename from vector/icons/apple-touch-icon-144x144.png rename to vector/vector-icons/apple-touch-icon-144x144.png diff --git a/vector/icons/apple-touch-icon-152x152.png b/vector/vector-icons/apple-touch-icon-152x152.png similarity index 100% rename from vector/icons/apple-touch-icon-152x152.png rename to vector/vector-icons/apple-touch-icon-152x152.png diff --git a/vector/icons/apple-touch-icon-180x180.png b/vector/vector-icons/apple-touch-icon-180x180.png similarity index 100% rename from vector/icons/apple-touch-icon-180x180.png rename to vector/vector-icons/apple-touch-icon-180x180.png diff --git a/vector/icons/apple-touch-icon-57x57.png b/vector/vector-icons/apple-touch-icon-57x57.png similarity index 100% rename from vector/icons/apple-touch-icon-57x57.png rename to vector/vector-icons/apple-touch-icon-57x57.png diff --git a/vector/icons/apple-touch-icon-60x60.png b/vector/vector-icons/apple-touch-icon-60x60.png similarity index 100% rename from vector/icons/apple-touch-icon-60x60.png rename to vector/vector-icons/apple-touch-icon-60x60.png diff --git a/vector/icons/apple-touch-icon-72x72.png b/vector/vector-icons/apple-touch-icon-72x72.png similarity index 100% rename from vector/icons/apple-touch-icon-72x72.png rename to vector/vector-icons/apple-touch-icon-72x72.png diff --git a/vector/icons/apple-touch-icon-76x76.png b/vector/vector-icons/apple-touch-icon-76x76.png similarity index 100% rename from vector/icons/apple-touch-icon-76x76.png rename to vector/vector-icons/apple-touch-icon-76x76.png diff --git a/vector/icons/apple-touch-icon-precomposed.png b/vector/vector-icons/apple-touch-icon-precomposed.png similarity index 100% rename from vector/icons/apple-touch-icon-precomposed.png rename to vector/vector-icons/apple-touch-icon-precomposed.png diff --git a/vector/icons/apple-touch-icon.png b/vector/vector-icons/apple-touch-icon.png similarity index 100% rename from vector/icons/apple-touch-icon.png rename to vector/vector-icons/apple-touch-icon.png diff --git a/vector/vector-icons/browserconfig.xml b/vector/vector-icons/browserconfig.xml new file mode 100644 index 0000000000..012a1dad28 --- /dev/null +++ b/vector/vector-icons/browserconfig.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<browserconfig> + <msapplication> + <tile> + <square70x70logo src="/vector-icons/mstile-70x70.png"/> + <square150x150logo src="/vector-icons/mstile-150x150.png"/> + <square310x310logo src="/vector-icons/mstile-310x310.png"/> + <wide310x150logo src="/vector-icons/mstile-310x150.png"/> + <TileColor>#da532c</TileColor> + </tile> + </msapplication> +</browserconfig> diff --git a/vector/icons/favicon-16x16.png b/vector/vector-icons/favicon-16x16.png similarity index 100% rename from vector/icons/favicon-16x16.png rename to vector/vector-icons/favicon-16x16.png diff --git a/vector/icons/favicon-32x32.png b/vector/vector-icons/favicon-32x32.png similarity index 100% rename from vector/icons/favicon-32x32.png rename to vector/vector-icons/favicon-32x32.png diff --git a/vector/icons/favicon-96x96.png b/vector/vector-icons/favicon-96x96.png similarity index 100% rename from vector/icons/favicon-96x96.png rename to vector/vector-icons/favicon-96x96.png diff --git a/vector/icons/favicon.ico b/vector/vector-icons/favicon.ico similarity index 100% rename from vector/icons/favicon.ico rename to vector/vector-icons/favicon.ico diff --git a/vector/icons/manifest.json b/vector/vector-icons/manifest.json similarity index 100% rename from vector/icons/manifest.json rename to vector/vector-icons/manifest.json diff --git a/vector/icons/mstile-144x144.png b/vector/vector-icons/mstile-144x144.png similarity index 100% rename from vector/icons/mstile-144x144.png rename to vector/vector-icons/mstile-144x144.png diff --git a/vector/icons/mstile-150x150.png b/vector/vector-icons/mstile-150x150.png similarity index 100% rename from vector/icons/mstile-150x150.png rename to vector/vector-icons/mstile-150x150.png diff --git a/vector/icons/mstile-310x150.png b/vector/vector-icons/mstile-310x150.png similarity index 100% rename from vector/icons/mstile-310x150.png rename to vector/vector-icons/mstile-310x150.png diff --git a/vector/icons/mstile-310x310.png b/vector/vector-icons/mstile-310x310.png similarity index 100% rename from vector/icons/mstile-310x310.png rename to vector/vector-icons/mstile-310x310.png diff --git a/vector/icons/mstile-70x70.png b/vector/vector-icons/mstile-70x70.png similarity index 100% rename from vector/icons/mstile-70x70.png rename to vector/vector-icons/mstile-70x70.png diff --git a/webpack.config.js b/webpack.config.js index 929e57d71b..c19438a0df 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,8 @@ var path = require('path'); var webpack = require('webpack'); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + +var olm_path = path.resolve('./node_modules/olm'); module.exports = { module: { @@ -9,6 +12,8 @@ module.exports = { loaders: [ { test: /\.json$/, loader: "json" }, { test: /\.js$/, loader: "babel", include: path.resolve('./src') }, + // css-raw-loader loads CSS but doesn't try to treat url()s as require()s + { test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, ] }, output: { @@ -29,15 +34,40 @@ module.exports = { // alias any requires to the react module to the one in our path, otherwise // we tend to get the react source included twice when using npm link. react: path.resolve('./node_modules/react'), + + // matrix-js-sdk will use olm if it is available, + // but does not explicitly depend on it. Pull it + // in from node_modules if it's there. + olm: olm_path, }, }, plugins: [ - new webpack.IgnorePlugin(/^olm/), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) } - }) + }), + + new ExtractTextPlugin("bundle.css", { + allChunks: true + }), + + // olm.js includes "require 'fs'", which is never + // executed in the browser. Ignore it. + new webpack.IgnorePlugin(/^fs$/, /node_modules\/olm$/) ], devtool: 'source-map' }; + +// ignore olm.js if it's not installed. +(function() { + var fs = require('fs'); + try { + fs.lstatSync(olm_path); + console.log("Olm is installed; including it in webpack bundle"); + } catch (e) { + module.exports.plugins.push( + new webpack.IgnorePlugin(/^olm$/) + ); + } +}) ();