Compare commits

..

1 Commits

Author SHA1 Message Date
Kegan Dougal
80277583db Fix timestamp hovers #59 2015-10-21 15:05:17 +01:00
295 changed files with 6504 additions and 9150 deletions

12
.gitignore vendored
View File

@@ -1,9 +1,3 @@
/cert.pem
/.DS_Store
/karma-reports
/key.pem
/lib
/node_modules
/packages/
/vector/bundle.*
/vector/components.css
node_modules
vector/bundle.*
lib

View File

@@ -1,14 +0,0 @@
{
"minify": true,
"classPrefix": "modernizr_",
"options": [
"setClasses"
],
"feature-detects": [
"test/css/displaytable",
"test/css/flexbox",
"test/es5/specification",
"test/css/objectfit",
"test/storage/localstorage"
]
}

View File

@@ -7,6 +7,3 @@ 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

View File

@@ -1,154 +0,0 @@
Changes in [0.7.2](https://github.com/vector-im/vector-web/releases/tag/v0.7.2) (2016-06-02)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.1...v0.7.2)
* Correctly bump the dep on new matrix-js-sdk and matrix-react-sdk
Changes in [0.7.1](https://github.com/vector-im/vector-web/releases/tag/v0.7.1) (2016-06-02)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.0...v0.7.1)
* Fix accidentally committed local changes to the default config.json (doh!)
Changes in [0.7.0](https://github.com/vector-im/vector-web/releases/tag/v0.7.0) (2016-06-02)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.6.1...v0.7.0)
* Update to matrix-react-sdk 0.6.0 - see
[changelog](https://github.com/matrix-org/matrix-react-sdk/blob/v0.6.0/CHANGELOG.md)
* Style selection color.
[\#1557](https://github.com/vector-im/vector-web/pull/1557)
* Fix NPE when loading the Settings page which infini-spinnered
[\#1518](https://github.com/vector-im/vector-web/pull/1518)
* Add option to enable email notifications
[\#1469](https://github.com/vector-im/vector-web/pull/1469)
Changes in [0.6.1](https://github.com/vector-im/vector-web/releases/tag/v0.6.1) (2016-04-22)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.6.0...v0.6.1)
* Update to matrix-react-sdk 0.5.2 - see
[changelog](https://github.com/matrix-org/matrix-react-sdk/blob/v0.5.2/CHANGELOG.md)
* Don't relayout scrollpanels every time something changes
[\#1438](https://github.com/vector-im/vector-web/pull/1438)
* Include react-addons-perf for non-production builds
[\#1431](https://github.com/vector-im/vector-web/pull/1431)
Changes in [0.6.0](https://github.com/vector-im/vector-web/releases/tag/v0.6.0) (2016-04-19)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.5.0...v0.6.0)
* Matthew/design tweaks
[\#1402](https://github.com/vector-im/vector-web/pull/1402)
* Improve handling of notification rules we can't parse
[\#1399](https://github.com/vector-im/vector-web/pull/1399)
* Do less mangling of jenkins builds
[\#1391](https://github.com/vector-im/vector-web/pull/1391)
* Start Notifications component refactor
[\#1386](https://github.com/vector-im/vector-web/pull/1386)
* make the UI fadable to help with decluttering
[\#1376](https://github.com/vector-im/vector-web/pull/1376)
* Get and display a user's pushers in settings
[\#1374](https://github.com/vector-im/vector-web/pull/1374)
* URL previewing support
[\#1343](https://github.com/vector-im/vector-web/pull/1343)
* 😄 Emoji autocomplete and unicode emoji to image conversion using emojione.
[\#1332](https://github.com/vector-im/vector-web/pull/1332)
* Show full-size avatar on MemberInfo avatar click
[\#1340](https://github.com/vector-im/vector-web/pull/1340)
* Numerous other changes via [matrix-react-sdk 0.5.1](https://github.com/matrix-org/matrix-react-sdk/blob/v0.5.1/CHANGELOG.md)
Changes in [0.5.0](https://github.com/vector-im/vector-web/releases/tag/v0.5.0) (2016-03-30)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.4.1...v0.5.0)
* Prettier, animated placeholder :D
[\#1292](https://github.com/vector-im/vector-web/pull/1292)
(Disabled for now due to high CPU usage)
* RoomDirectory: use SimpleRoomHeader instead of RoomHeader
[\#1307](https://github.com/vector-im/vector-web/pull/1307)
* Tell webpack not to parse the highlight.js languages
[\#1277](https://github.com/vector-im/vector-web/pull/1277)
* CSS for https://github.com/matrix-org/matrix-react-sdk/pull/247
[\#1249](https://github.com/vector-im/vector-web/pull/1249)
* URI-decode the hash-fragment
[\#1254](https://github.com/vector-im/vector-web/pull/1254)
Changes in [0.4.1](https://github.com/vector-im/vector-web/releases/tag/v0.4.1) (2016-03-23)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.4.0...v0.4.1)
* Update to matrix-react-sdk 0.3.1; see
https://github.com/matrix-org/matrix-react-sdk/blob/v0.3.1/CHANGELOG.md
(Disables debug logging)
Changes in [0.4.0](https://github.com/vector-im/vector-web/releases/tag/v0.4.0) (2016-03-23)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.3.0...v0.4.0)
* Update to matrix-react-sdk 0.3.0; see
https://github.com/matrix-org/matrix-react-sdk/blob/master/CHANGELOG.md
Other changes
* permalink button
[\#1232](https://github.com/vector-im/vector-web/pull/1232)
* make senderprofiles clickable
[\#1191](https://github.com/vector-im/vector-web/pull/1191)
* fix notif spam when logging in from a guest session by correctly logging out
first.
[\#1180](https://github.com/vector-im/vector-web/pull/1180)
* use new start_login_from_guest dispatch for cancellable logins from guest
accounts
[\#1165](https://github.com/vector-im/vector-web/pull/1165)
* Use then() chaining rather than manual callbacks
[\#1171](https://github.com/vector-im/vector-web/pull/1171)
* Remove trailing whitespace
[\#1163](https://github.com/vector-im/vector-web/pull/1163)
* Update the actions of default rules instead of overriding.
[\#1037](https://github.com/vector-im/vector-web/pull/1037)
* Update README to include `npm install` in react-sdk
[\#1137](https://github.com/vector-im/vector-web/pull/1137)
Changes in vector v0.3.0 (2016-03-11)
======================================
* Lots of new bug fixes and updates
Changes in vector v0.2.0 (2016-02-24)
======================================
* Refactor of matrix-react-sdk and vector to remove separation between views and
controllers
* Temporarily break the layering abstraction between vector and matrix-react-sdk
for expedience in developing vector.
* Vast numbers of new features, including read receipts, read-up-to markers,
updated look and feel, search, new room and user settings, and email invites.
Changes in vector v0.1.2 (2015-10-28)
======================================
* Support Room Avatars
* Fullscreen video calls
* Mute mic in VoIP calls
* Fix bug with multiple desktop notifications
* Context menu on messages
* Better hover-over on member list
* Support CAS auth
* Many other bug fixes
Changes in vector v0.1.1 (2015-08-10)
======================================
* Support logging in with an email address
* Use the Vector identity server
* Fix a bug where the client was not stopped properly on logout
* Fix bugs where field values would be forgotten if login or registration failed
* Improve URL bar navigation
* Add explanatory help text on advanced server options
* Fix a bug which caused execptions on malformed VoIP invitations
* Remove superfluous scrollbars on Firefox
* Numerous CSS fixes
* Improved accessibility
* Support command-click / middle click to open image in a new tab
* Improved room directory
* Fix display of text with many combining unicode points
Changes in vector v0.1.0 (2015-08-10)
======================================
Initial release

20
CHANGES.rst Normal file
View File

@@ -0,0 +1,20 @@
Changes in vector v0.1.1 (2015-08-10)
======================================
* Support logging in with an email address
* Use the Vector identity server
* Fix a bug where the client was not stopped properly on logout
* Fix bugs where field values would be forgotten if login or registration failed
* Improve URL bar navigation
* Add explanatory help text on advanced server options
* Fix a bug which caused execptions on malformed VoIP invitations
* Remove superfluous scrollbars on Firefox
* Numerous CSS fixes
* Improved accessibility
* Support command-click / middle click to open image in a new tab
* Improved room directory
* Fix display of text with many combining unicode points
Changes in vector v0.1.0 (2015-08-10)
======================================
Initial release

142
README.md
View File

@@ -7,137 +7,39 @@ Getting started
===============
1. Install or update `node.js` so that your `npm` is at least at version `2.0.0`
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. If you are using the `develop` branch of vector, you will probably need to
rebuild one of the dependencies, due to https://github.com/npm/npm/issues/3055:
`(cd node_modules/matrix-react-sdk && 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.
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.
With `npm start`, any changes you make to the source files will cause a rebuild so
your changes will show up when you refresh. This development server also disables
caching, so do NOT use it in production.
your changes will show up when you refresh.
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
==========
On a Unix-based OS, run `npm run package` to build a tarball package. Untaring
this file will give a version-specific directory containing all the files that
need to go on your web server.
The package script is not supported on Windows, so Windows users can run `npm
run build`, which will build all the necessary files into the `vector`
directory. Note that the version of Vector will not appear in Settings without
using the package script. You can then mount the vector directory on your
webserver to actually serve up the app, which is entirely static content.
For production use, run `npm run build` to build all the necessary files
into the `vector` directory and run your own server.
Development
===========
You can work on any of the source files within Vector with the setup above,
and your changes will cause an instant rebuild. If you also need to make
changes to the react sdk, you can:
For simple tweaks, you can work on any of the source files within Vector with
the setup above, and your changes will cause an instant rebuild.
However, much of the functionality in Vector is actually in the
`matrix-react-sdk` and `matrix-js-sdk` modules. It is possible to set these up
in a way that makes it easy to track the `develop` branches in git and to make
local changes without having to manually rebuild each time.
[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`
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`
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.
1. Link the react sdk package into the example:
`npm link path/to/your/react/sdk`
2. Start the development rebuilder in your react SDK directory:
`npm start`
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`.
Enabling encryption
===================
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.
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.
Deployment
==========
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.
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 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.

View File

@@ -1,7 +1,4 @@
{
"default_hs_url": "https://matrix.org",
"default_is_url": "https://vector.im",
"brand": "Vector",
"integrations_ui_url": "http://localhost:8081/",
"integrations_rest_url": "http://localhost:5050"
"default_is_url": "https://vector.im"
}

View File

@@ -1,170 +0,0 @@
#!/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)

View File

@@ -1,33 +0,0 @@
#!/bin/bash
set -e
export NVM_DIR="/home/jenkins/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 4
set -x
npm install
# we may be using a dev branch of react-sdk, in which case we need to build it
(cd node_modules/matrix-react-sdk && npm run build)
# run the mocha tests
npm run test
# build our artifacts; dumps them in ./vector
npm run build:dev
# gzip up ./vector
rm vector-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist
# node_modules deps from 'npm install' don't have a .git dir so can't
# rev-parse; but they do set the commit in package.json under 'gitHead' which
# we're grabbing here.
REACT_SHA=$(grep 'gitHead' node_modules/matrix-react-sdk/package.json | cut -d \" -f 4 | head -c 12)
JSSDK_SHA=$(grep 'gitHead' node_modules/matrix-js-sdk/package.json | cut -d \" -f 4 | head -c 12)
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)

View File

@@ -1,136 +0,0 @@
// karma.conf.js - the config file for karma, which runs our tests.
var path = require('path');
var webpack = require('webpack');
/*
* We use webpack to build our tests. It's a pain to have to wait for webpack
* to build everything; however it's the easiest way to load our dependencies
* from node_modules.
*
* If you run karma in multi-run mode (with `npm run test:multi`), it will watch
* the tests for changes, and webpack will rebuild using a cache. This is much quicker
* than a clean rebuild.
*/
// the name of the test file. By default, a special file which runs all tests.
var testFile = process.env.KARMA_TEST_FILE || 'test/all-tests.js';
process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs';
process.env.Q_DEBUG = 1;
module.exports = function (config) {
config.set({
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha'],
// list of files / patterns to load in the browser
files: [
testFile,
{pattern: 'vector/img/*', watched: false, included: false, served: true, nocache: false},
],
// redirect img links to the karma server
proxies: {
"/img/": "/base/vector/img/",
},
// preprocess matching files before serving them to the browser
// available preprocessors:
// https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test/**/*.js': ['webpack', 'sourcemap']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'junit'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR ||
// config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file
// changes
autoWatch: true,
// start these browsers
// available browser launchers:
// https://npmjs.org/browse/keyword/karma-launcher
browsers: [
'Chrome',
//'PhantomJS',
],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
junitReporter: {
outputDir: 'karma-reports',
},
webpack: {
module: {
loaders: [
{ test: /\.json$/, loader: "json" },
{
test: /\.js$/, loader: "babel",
include: [path.resolve('./src'),
path.resolve('./test'),
],
query: {
// we're using babel 5, for consistency with
// the release build, which doesn't use the
// presets.
// presets: ['react', 'es2015'],
},
},
],
noParse: [
// don't parse the languages within highlight.js. They
// cause stack overflows
// (https://github.com/webpack/webpack/issues/1721), and
// there is no need for webpack to parse them - they can
// just be included as-is.
/highlight\.js\/lib\/languages/,
// also disable parsing for sinon, because it
// tries to do voodoo with 'require' which upsets
// webpack (https://github.com/webpack/webpack/issues/304)
/sinon\/pkg\/sinon\.js$/,
],
},
resolve: {
alias: {
// 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'),
// same goes for js-sdk
"matrix-js-sdk": path.resolve('./node_modules/matrix-js-sdk'),
sinon: 'sinon/pkg/sinon.js',
},
root: [
path.resolve('./src'),
path.resolve('./test'),
],
},
devtool: 'inline-source-map',
},
});
};

View File

@@ -1,6 +1,6 @@
{
"name": "vector-web",
"version": "0.7.2",
"version": "0.0.1",
"description": "Vector webapp",
"author": "matrix.org",
"repository": {
@@ -9,73 +9,40 @@
},
"license": "Apache-2.0",
"style": "bundle.css",
"matrix-react-parent": "matrix-react-sdk",
"scripts": {
"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",
"reskindex": "reskindex vector -h src/skins/vector/header",
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --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:bundle:dev": "NODE_ENV=production webpack --optimize-occurence-order lib/vector/index.js vector/bundle.js",
"build": "npm run build:css && npm run build:compile && npm run build:bundle",
"build:dev": "npm run build:css && npm run build:compile && npm run build:bundle:dev",
"package": "scripts/package.sh",
"start:js": "webpack -w src/vector/index.js vector/bundle.js",
"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",
"start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.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\"",
"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",
"test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
"test:multi": "karma start"
"clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map",
"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": "matrix-org/gemini-scrollbar#87ebaa7",
"gfm.css": "^1.1.1",
"highlight.js": "^9.0.0",
"linkifyjs": "^2.0.0-beta.4",
"matrix-js-sdk": "^0.5.4",
"matrix-react-sdk": "^0.6.2",
"modernizr": "^3.1.0",
"matrix-js-sdk": "^0.2.2",
"matrix-react-sdk": "^0.0.1",
"q": "^1.4.1",
"react": "^15.0.1",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.0.1",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#c3d942e",
"sanitize-html": "^1.11.1"
"react": "^0.13.3",
"react-loader": "^1.4.0"
},
"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",
"expect": "^1.16.0",
"http-server": "^0.8.4",
"json-loader": "^0.5.3",
"karma": "^0.13.22",
"karma-chrome-launcher": "^0.2.3",
"karma-cli": "^0.1.2",
"karma-junit-reporter": "^0.4.1",
"karma-mocha": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.7.0",
"mocha": "^2.4.5",
"parallelshell": "^1.2.0",
"phantomjs-prebuilt": "^2.1.7",
"react-addons-test-utils": "^15.0.1",
"react-addons-perf": "^15.0",
"rimraf": "^2.4.3",
"source-map-loader": "^0.1.5",
"webpack": "^1.12.14"
"uglifycss": "0.0.15"
}
}

View File

@@ -1,12 +0,0 @@
#!/bin/sh
#
# Script to perform a release of vector-web.
#
# Requires github-changelog-generator; to install, do
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
set -e
cd `dirname $0`
exec ./node_modules/matrix-js-sdk/release.sh -z "$@"

View File

@@ -1,124 +0,0 @@
#!/usr/bin/env perl
use warnings;
use strict;
use Net::GitHub;
use DateTime;
use DateTime::Format::ISO8601;
my $gh = Net::GitHub->new(
login => 'ara4n', pass => 'secret'
);
$gh->set_default_user_repo('vector-im', 'vector-web');
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
while ($gh->issue->has_next_page) {
push @issues, $gh->issue->next_page;
}
# we want:
# day by day:
# split by { open, closed }
# split by { bug, feature, neither }
# each split by { p1, p2, p3, p4, p5, unprioritised } <- priority
# each split by { minor, major, critical, cosmetic, network, no-severity } <- severity
# then split (with overlap between the groups) as { total, tag1, tag2, ... }?
# ...and then all over again split by milestone.
my $days = {};
my $schema = {};
my $now = DateTime->now();
foreach my $issue (@issues) {
next if ($issue->{pull_request});
# use Data::Dumper;
# print STDERR Dumper($issue);
my @label_list = map { $_->{name} } @{$issue->{labels}};
my $labels = {};
$labels->{$_} = 1 foreach (@label_list);
$labels->{bug}++ if ($labels->{cosmetic} && !$labels->{bug} && !$labels->{feature});
my $extract_labels = sub {
my $label = undef;
foreach (@_) {
$label ||= $_ if (delete $labels->{$_});
}
return $label;
};
my $state = $issue->{state};
my $type = &$extract_labels(qw(bug feature)) || "neither";
my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
do {
my $ymd = $start->ymd();
$days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
foreach (keys %$labels) {
$days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
}
$start = $start->add(days => 1);
} while (DateTime->compare($start, $now) < 0);
if ($state eq 'closed') {
my $end = DateTime::Format::ISO8601->parse_datetime($issue->{closed_at});
do {
my $ymd = $end->ymd();
$days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
foreach (keys %$labels) {
$days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
}
$end = $end->add(days => 1);
} while (DateTime->compare($end, $now) < 0);
}
}
print "day,";
foreach my $state (sort keys %{$schema}) {
foreach my $type (grep { /^(bug|feature)$/ } sort keys %{$schema->{$state}}) {
foreach my $priority (grep { /^(p1|p2)$/ } sort keys %{$schema->{$state}->{$type}}) {
foreach my $severity (sort keys %{$schema->{$state}->{$type}->{$priority}}) {
# foreach my $tag (sort keys %{$schema->{$state}->{$type}->{$priority}->{$severity}}) {
# print "\"$type\n$priority\n$severity\n$tag\",";
# }
print "\"$state\n$type\n$priority\n$severity\",";
}
}
}
}
print "\n";
foreach my $day (sort keys %$days) {
print "$day,";
foreach my $state (sort keys %{$schema}) {
foreach my $type (grep { /^(bug|feature)$/ } sort keys %{$schema->{$state}}) {
foreach my $priority (grep { /^(p1|p2)$/ } sort keys %{$schema->{$state}->{$type}}) {
foreach my $severity (sort keys %{$schema->{$state}->{$type}->{$priority}}) {
# foreach my $tag (sort keys %{$schema->{$state}->{$type}->{$priority}->{$severity}}) {
# print $days->{$day}->{$state}->{$type}->{$priority}->{$severity}->{$tag} || 0;
# print ",";
# }
print $days->{$day}->{$state}->{$type}->{$priority}->{$severity}->{total} || 0;
print ",";
}
}
}
}
print "\n";
}

View File

@@ -1,104 +0,0 @@
#!/usr/bin/env perl
use warnings;
use strict;
use Net::GitHub;
use DateTime;
use DateTime::Format::ISO8601;
my $gh = Net::GitHub->new(
login => 'ara4n', pass => 'secret'
);
$gh->set_default_user_repo('vector-im', 'vector-web');
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
while ($gh->issue->has_next_page) {
push @issues, $gh->issue->next_page;
}
# we want:
# day by day:
# split by { open, closed }
# split by { bug, feature, neither }
# each split by { p1, p2, p3, p4, p5, unprioritised } <- priority
# each split by { minor, major, critical, cosmetic, network, no-severity } <- severity
# then split (with overlap between the groups) as { total, tag1, tag2, ... }?
# ...and then all over again split by milestone.
my $days = {};
my $schema = {};
my $now = DateTime->now();
foreach my $issue (@issues) {
next if ($issue->{pull_request});
use Data::Dumper;
print STDERR Dumper($issue);
my @label_list = map { $_->{name} } @{$issue->{labels}};
my $labels = {};
$labels->{$_} = 1 foreach (@label_list);
$labels->{bug}++ if ($labels->{cosmetic} && !$labels->{bug} && !$labels->{feature});
my $extract_labels = sub {
my $label = undef;
foreach (@_) {
$label ||= $_ if (delete $labels->{$_});
}
return $label;
};
my $type = &$extract_labels(qw(bug feature)) || "neither";
my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
my $end = $issue->{closed_at} ? DateTime::Format::ISO8601->parse_datetime($issue->{closed_at}) : $now;
do {
my $ymd = $start->ymd();
$days->{ $ymd }->{ $type }->{ $priority }->{ $severity }->{ total }++;
$schema->{ $type }->{ $priority }->{ $severity }->{ total }++;
foreach (keys %$labels) {
$days->{ $ymd }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
$schema->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
}
$start = $start->add(days => 1);
} while (DateTime->compare($start, $end) < 0);
}
print "day,";
foreach my $type (sort keys %{$schema}) {
foreach my $priority (sort keys %{$schema->{$type}}) {
foreach my $severity (sort keys %{$schema->{$type}->{$priority}}) {
# foreach my $tag (sort keys %{$schema->{$type}->{$priority}->{$severity}}) {
# print "\"$type\n$priority\n$severity\n$tag\",";
# }
print "\"$type\n$priority\n$severity\",";
}
}
}
print "\n";
foreach my $day (sort keys %$days) {
print "$day,";
foreach my $type (sort keys %{$schema}) {
foreach my $priority (sort keys %{$schema->{$type}}) {
foreach my $severity (sort keys %{$schema->{$type}->{$priority}}) {
# foreach my $tag (sort keys %{$schema->{$type}->{$priority}->{$severity}}) {
# print $days->{$day}->{$type}->{$priority}->{$severity}->{$tag} || 0;
# print ",";
# }
print $days->{$day}->{$type}->{$priority}->{$severity}->{total} || 0;
print ",";
}
}
}
print "\n";
}

View File

@@ -1,15 +0,0 @@
#!/bin/sh
set -e
version=`git describe --dirty --tags || echo unknown`
npm run build
mkdir -p packages
cp -r vector vector-$version
echo $version > vector-$version/version
tar chvzf packages/vector-$version.tar.gz vector-$version
rm -r vector-$version
echo
echo "Packaged packages/vector-$version.tar.gz"

53
src/Avatar.js Normal file
View File

@@ -0,0 +1,53 @@
/*
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=";
}
}
}

71
src/ContextualMenu.js Normal file
View File

@@ -0,0 +1,71 @@
/*
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,
right: props.right + 8,
};
// 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="mx_ContextualMenu_wrapper">
<div className="mx_ContextualMenu" style={position}>
<img className="mx_ContextualMenu_chevron" src="img/chevron-right.png" width="9" height="16" />
<Element {...props} onFinished={closeMenu}/>
</div>
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
</div>
);
React.render(menu, this.getOrCreateContainer());
return {close: closeMenu};
},
};

View File

@@ -1,54 +0,0 @@
/*
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.
*
* To update it, run:
* ./reskindex.js -h header
*/
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.SearchBox'] = require('./components/structures/SearchBox');
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');

View File

@@ -1,66 +0,0 @@
/*
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>
Vector uses many advanced browser features, some of which are not
available or experimental in your current browser.
</p>
<p>
Please install <a href="https://www.google.com/chrome">Chrome</a> for
the best experience. <a href="https://getfirefox.com">Firefox</a>,
<a href="http://apple.com/safari">Safari</a> and
<a href="http://opera.com">Opera</a> work too.
</p>
<p>
With your current browser, the look and feel of the application may
be completely incorrect, and some or all features may not function.
If you want to try it anyway you can continue, but you are on your own
in terms of any issues you may encounter!
</p>
<button onClick={this.onAccept}>
I understand the risks and wish to continue
</button>
</div>
</div>
);
}
});

View File

@@ -1,133 +0,0 @@
/*
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,
searchFilter: '',
};
},
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.groupRoomId || call.roomId,
});
}
},
onSearch: function(term) {
this.setState({ searchFilter: term });
},
render: function() {
var RoomList = sdk.getComponent('rooms.RoomList');
var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
var SearchBox = sdk.getComponent('structures.SearchBox');
var collapseButton;
var classes = "mx_LeftPanel mx_fadable";
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} style={{ opacity: this.props.opacity }}>
<SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />
{ collapseButton }
{ callPreview }
<RoomList
selectedRoom={this.props.selectedRoom}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ConferenceHandler={VectorConferenceHandler} />
<BottomLeftMenu collapsed={this.props.collapsed}/>
</aside>
);
}
});
module.exports = DragDropContext(HTML5Backend)(LeftPanel);

View File

@@ -1,181 +0,0 @@
/*
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") {
dis.dispatch({
action: 'show_right_panel',
});
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} />
}
}
}
if (!panel) {
panel = <div className="mx_RightPanel_blank"></div>;
}
var classes = "mx_RightPanel mx_fadable";
if (this.props.collapsed) {
classes += " collapsed";
}
return (
<aside className={classes} style={{ opacity: this.props.opacity }}>
<div className="mx_RightPanel_header">
{ buttonGroup }
</div>
{ panel }
<div className="mx_RightPanel_footer">
</div>
</aside>
);
}
});

View File

@@ -1,228 +0,0 @@
/*
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,
}
},
componentWillMount: function() {
// dis.dispatch({
// action: 'ui_opacity',
// sideOpacity: 0.3,
// middleOpacity: 0.3,
// });
},
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();
}
});
},
componentWillUnmount: function() {
// dis.dispatch({
// action: 'ui_opacity',
// sideOpacity: 1.0,
// middleOpacity: 1.0,
// });
},
showRoom: function(roomId) {
// extract the metadata from the publicRooms structure to pass
// as out-of-band data to view_room, because we get information
// here that we can't get other than by joining the room in some
// cases.
var room;
for (var i = 0; i < this.state.publicRooms.length; ++i) {
if (this.state.publicRooms[i].room_id == roomId) {
room = this.state.publicRooms[i];
break;
}
}
var oob_data = {};
if (room) {
if (MatrixClientPeg.get().isGuest()) {
if (!room.world_readable && !room.guest_can_join) {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
title: "Failed to join the room",
description: "This room is inaccessible to guests. You may be able to join if you register."
});
return;
}
}
oob_data = {
avatarUrl: room.avatar_url,
// XXX: This logic is duplicated from the JS SDK which
// would normally decide what the name is.
name: room.name || room.canonical_alias || (room.aliases ? room.aliases[0] : "Unnamed room"),
};
}
dis.dispatch({
action: 'view_room',
room_id: roomId,
oob_data: oob_data,
});
},
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.name && a.name.toLowerCase().search(filter.toLowerCase()) >= 0) ||
(a.aliases && 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 alias = rooms[i].canonical_alias || (rooms[i].aliases ? rooms[i].aliases[0] : "");
var name = rooms[i].name || alias || "Unnamed room";
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>&nbsp;
{ perms }
<div className="mx_RoomDirectory_topic"
onClick={ function(e) { e.stopPropagation() } }
dangerouslySetInnerHTML={{ __html: topic }}/>
<div className="mx_RoomDirectory_alias">{ alias }</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 SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
return (
<div className="mx_RoomDirectory">
<SimpleRoomHeader title="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"
relayoutOnUpdate={false} >
<table ref="directory_table" className="mx_RoomDirectory_table">
<tbody>
{ this.getRows(this.state.roomAlias) }
</tbody>
</table>
</GeminiScrollbar>
</div>
</div>
);
}
});

View File

@@ -1,367 +0,0 @@
/*
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
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
onHeaderClick: React.PropTypes.func,
alwaysShowHeader: React.PropTypes.bool,
incomingCall: React.PropTypes.object,
onShowMoreRooms: React.PropTypes.func,
searchFilter: React.PropTypes.string,
},
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.applySearchFilter(this.props.list, this.props.searchFilter), 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(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
},
applySearchFilter: function(list, filter) {
if (filter === "") return list;
return list.filter((room) => {
return room.name && room.name.toLowerCase().indexOf(filter.toLowerCase()) >= 0
});
},
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 }
<img 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);

View File

@@ -1,119 +0,0 @@
/*
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 rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
module.exports = React.createClass({
displayName: 'SearchBox',
propTypes: {
collapsed: React.PropTypes.bool,
onSearch: React.PropTypes.func,
},
getInitialState: function() {
return {
searchTerm: "",
};
},
onChange: function() {
if (!this.refs.search) return;
this.setState({ searchTerm: this.refs.search.value });
this.onSearch();
},
onSearch: new rate_limited_func(
function() {
this.props.onSearch(this.refs.search.value);
},
100
),
onToggleCollapse: function(show) {
if (show) {
dis.dispatch({
action: 'show_left_panel',
});
}
else {
dis.dispatch({
action: 'hide_left_panel',
});
}
},
render: function() {
var TintableSvg = sdk.getComponent('elements.TintableSvg');
var toggleCollapse;
if (this.props.collapsed) {
toggleCollapse =
<div className="mx_SearchBox_maximise" onClick={ this.onToggleCollapse.bind(this, true) }>
<TintableSvg src="img/maximise.svg" width="10" height="16" alt="&lt;"/>
</div>
}
else {
toggleCollapse =
<div className="mx_SearchBox_minimise" onClick={ this.onToggleCollapse.bind(this, false) }>
<TintableSvg src="img/minimise.svg" width="10" height="16" alt="&lt;"/>
</div>
}
var searchControls;
if (!this.props.collapsed) {
searchControls = [
this.state.searchTerm.length > 0 ?
<div key="button"
className="mx_SearchBox_closeButton"
onClick={ ()=>{ this.refs.search.value = ""; this.onChange(); } }>
<TintableSvg
className="mx_SearchBox_searchButton"
src="img/icons-close.svg" width="24" height="24"
/>
</div>
:
<TintableSvg
key="button"
className="mx_SearchBox_searchButton"
src="img/right_search.svg" width="24" height="24"
/>,
<input
key="searchfield"
type="text"
ref="search"
className="mx_SearchBox_search"
value={ this.state.searchTerm }
onChange={ this.onChange }
placeholder="Search room names"
/>
];
}
var self = this;
return (
<div className="mx_SearchBox">
{ searchControls }
{ toggleCollapse }
</div>
);
}
});

View File

@@ -1,54 +0,0 @@
/*
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>
);
}
});

View File

@@ -1,189 +0,0 @@
/*
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: {
src: React.PropTypes.string.isRequired, // the source of the image being displayed
name: React.PropTypes.string, // the main title ('name') for the image
link: React.PropTypes.string, // the link (if any) applied to the name of the image
width: React.PropTypes.number, // width of the image src in pixels
height: React.PropTypes.number, // height of the image src in pixels
fileSize: React.PropTypes.number, // size of the image src in bytes
onFinished: React.PropTypes.func.isRequired, // callback when the lightbox is dismissed
// the event (if any) that the Image is displaying. Used for event-specific stuff like
// redactions, senders, timestamps etc. Other descriptors are taken from the explicit
// properties above, which let us use lightboxes to display images which aren't associated
// with events.
mxEvent: React.PropTypes.object,
},
// 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 + ")"
});
});
},
getName: function () {
var name = this.props.name;
if (name && this.props.link) {
name = <a href={ this.props.link } target="_blank">{ name }</a>;
}
return name;
},
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.fileSize) {
size = filesize(this.props.fileSize);
}
var size_res;
if (size && res) {
size_res = size + ", " + res;
}
else {
size_res = size || res;
}
var showEventMeta = !!this.props.mxEvent;
var eventMeta;
if(showEventMeta) {
eventMeta = (<div className="mx_ImageView_metadata">
Uploaded on { DateUtils.formatDate(new Date(this.props.mxEvent.getTs())) } by { this.props.mxEvent.getSender() }
</div>);
}
var eventRedact;
if(showEventMeta) {
eventRedact = (<div className="mx_ImageView_button" onClick={this.onRedactClick}>
Redact
</div>);
}
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.getName() }
</div>
{ eventMeta }
<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>
{ eventRedact }
<div className="mx_ImageView_shim">
</div>
</div>
</div>
</div>
<div className="mx_ImageView_rhs">
</div>
</div>
);
}
});

View File

@@ -1,43 +0,0 @@
/*
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': 'start_upgrade_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>
);
}
});

View File

@@ -1,156 +0,0 @@
/*
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 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',
propTypes: {
/* the MatrixEvent associated with the context menu */
mxEvent: React.PropTypes.object.isRequired,
/* an optional EventTileOps implementation that can be used to unhide preview widgets */
eventTileOps: React.PropTypes.object,
/* callback called when the menu is dismissed */
onFinished: React.PropTypes.func,
},
onResendClick: function() {
Resend.resend(this.props.mxEvent);
if (this.props.onFinished) this.props.onFinished();
},
onViewSourceClick: function() {
var ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createDialog(ViewSource, {
mxEvent: this.props.mxEvent
});
if (this.props.onFinished) this.props.onFinished();
},
onRedactClick: function() {
MatrixClientPeg.get().redactEvent(
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
).done(function() {
// message should disappear by itself
}, 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 message. (" + code + ")"
});
});
if (this.props.onFinished) this.props.onFinished();
},
onCancelSendClick: function() {
Resend.removeFromQueue(this.props.mxEvent);
if (this.props.onFinished) this.props.onFinished();
},
onPermalinkClick: function() {
if (this.props.onFinished) this.props.onFinished();
},
onUnhidePreviewClick: function() {
if (this.props.eventTileOps) {
this.props.eventTileOps.unhideWidget();
}
if (this.props.onFinished) this.props.onFinished();
},
render: function() {
var eventStatus = this.props.mxEvent.status;
var resendButton;
var viewSourceButton;
var redactButton;
var cancelButton;
var permalinkButton;
var unhidePreviewButton;
if (eventStatus === 'not_sent') {
resendButton = (
<div className="mx_ContextualMenu_field" onClick={this.onResendClick}>
Resend
</div>
);
}
if (!eventStatus) { // sent
redactButton = (
<div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
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
</div>
);
if (this.props.eventTileOps) {
if (this.props.eventTileOps.isWidgetHidden()) {
unhidePreviewButton = (
<div className="mx_ContextualMenu_field" onClick={this.onUnhidePreviewClick}>
Unhide Preview
</div>
)
}
}
// XXX: this should be https://matrix.to.
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
permalinkButton = (
<div className="mx_ContextualMenu_field">
<a href={ "#/room/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() }
onClick={ this.onPermalinkClick }>Permalink</a>
</div>
);
return (
<div>
{resendButton}
{redactButton}
{cancelButton}
{viewSourceButton}
{unhidePreviewButton}
{permalinkButton}
</div>
);
}
});

View File

@@ -1,205 +0,0 @@
/*
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';

View File

@@ -1,69 +0,0 @@
/*
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>
);
}
});

View File

@@ -1,867 +0,0 @@
/*
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');
var configJson = require("../../../../config.json");
var notifications = require('../../../notifications');
// TODO: this "view" component still has far to much application logic in it,
// which should be factored out to other files.
// TODO: this component also does a lot of direct poking into this.state, which
// is VERY NAUGHTY.
var NotificationUtils = notifications.NotificationUtils;
var VectorPushRulesDefinitions = notifications.VectorPushRulesDefinitions;
var PushRuleVectorState = notifications.PushRuleVectorState;
var ContentRules = notifications.ContentRules;
/**
* Rules that Vector used to set in order to override the actions of default rules.
* These are used to port peoples existing overrides to match the current API.
* These can be removed and forgotten once everyone has moved to the new client.
*/
var LEGACY_RULES = {
"im.vector.rule.contains_display_name": ".m.rule.contains_display_name",
"im.vector.rule.room_one_to_one": ".m.rule.room_one_to_one",
"im.vector.rule.room_message": ".m.rule.message",
"im.vector.rule.invite_for_me": ".m.rule.invite_for_me",
"im.vector.rule.call": ".m.rule.call",
"im.vector.rule.notices": ".m.rule.suppress_notices"
};
function portLegacyActions(actions) {
var decoded = NotificationUtils.decodeActions(actions);
if (decoded !== null) {
return NotificationUtils.encodeActions(decoded);
} else {
// We don't recognise one of the actions here, so we don't try to
// canonicalise them.
return actions;
}
}
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
},
propTypes: {
// The array of threepids from the JS SDK (required for email notifications)
threepids: React.PropTypes.array.isRequired,
},
getDefaultProps: function() {
return {
threepids: []
};
},
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() {
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);
},
onEnableEmailNotificationsChange: function(address, event) {
var emailPusherPromise;
if (event.target.checked) {
var data = {}
if (configJson.brand) {
data['brand'] = configJson.brand;
}
emailPusherPromise = UserSettingsStore.addEmailPusher(address, data);
} else {
var emailPusher = UserSettingsStore.getEmailPusher(this.state.pushers, address);
emailPusher.kind = null;
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
}
emailPusherPromise.done(() => {
this._refreshFromServer();
}, (error) => {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Error saving email notification preferences",
description: "Vector was unable to save your email notification preferences.",
});
});
},
onNotifStateButtonClicked: function(event) {
// FIXME: use .bind() rather than className metadata here surely
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;
}
}
},
_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) {
var actions = ruleDefinition.vectorStateToActions[newPushRuleVectorState];
if (!actions) {
// The new state corresponds to disabling the rule.
deferreds.push(cli.setPushRuleEnabled('global', rule.rule.kind, rule.rule.rule_id, false));
}
else {
// The new state corresponds to enabling the rule and setting specific actions
deferreds.push(this._updatePushRuleActions(rule.rule, actions, true));
}
}
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 = PushRuleVectorState.actionsFor(PushRuleVectorState.ON);
}
if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) {
enabled = true;
}
break;
case PushRuleVectorState.LOUD:
if (rule.actions.length !== 3) {
actions = PushRuleVectorState.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 = PushRuleVectorState.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: PushRuleVectorState.actionsFor(pushRuleVectorStateKind),
pattern: keyword
}));
}
else {
deferreds.push(self._addDisabledPushRule('global', 'content', keyword, {
actions: PushRuleVectorState.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();
return cli.addPushRule(scope, kind, ruleId, body).then(function() {
return cli.setPushRuleEnabled(scope, kind, ruleId, false);
});
},
// Check if any legacy im.vector rules need to be ported to the new API
// for overriding the actions of default rules.
_portRulesToNewAPI: function(rulesets) {
var self = this;
var needsUpdate = [];
var cli = MatrixClientPeg.get();
for (var kind in rulesets.global) {
var ruleset = rulesets.global[kind];
for (var i = 0; i < ruleset.length; ++i) {
var rule = ruleset[i];
if (rule.rule_id in LEGACY_RULES) {
console.log("Porting legacy rule", rule);
needsUpdate.push( function(kind, rule) {
return cli.setPushRuleActions(
'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions)
).then( function() {
return cli.deletePushRule('global', kind, rule.rule_id);
})
}(kind, rule));
}
}
}
if (needsUpdate.length > 0) {
// If some of the rules need to be ported then wait for the porting
// to happen and then fetch the rules again.
return q.allSettled(needsUpdate).then( function() {
return cli.getPushRules();
});
} else {
// Otherwise return the rules that we already have.
return rulesets;
}
},
_refreshFromServer: function() {
var self = this;
var pushRulesPromise = MatrixClientPeg.get().getPushRules().then(self._portRulesToNewAPI).then(function(rulesets) {
//console.log("resolving pushRulesPromise");
/// XXX seriously? wtf is this?
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: []};
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 === 'vector') {
defaultRules.vector[r.rule_id] = r;
}
else if (cat === 'master') {
defaultRules.master.push(r);
}
else {
defaultRules['others'].push(r);
}
}
}
}
// Get the master rule if any defined by the hs
if (defaultRules.master.length > 0) {
self.state.masterPushRule = defaultRules.master[0];
}
// parse the keyword rules into our state
var contentRules = ContentRules.parseContentRules(rulesets);
self.state.vectorContentRules = {
vectorState: contentRules.vectorState,
rules: contentRules.rules,
};
self.state.externalContentRules = contentRules.externalRules;
// Build the rules displayed in the Vector UI matrix table
self.state.vectorPushRules = [];
self.state.externalPushRules = [];
var vectorRuleIds = [
'.m.rule.contains_display_name',
'_keywords',
'.m.rule.room_one_to_one',
'.m.rule.message',
'.m.rule.invite_for_me',
//'im.vector.rule.member_event',
'.m.rule.call',
'.m.rule.suppress_notices'
];
for (var i in vectorRuleIds) {
var vectorRuleId = vectorRuleIds[i];
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 ruleDefinition = VectorPushRulesDefinitions[vectorRuleId];
var rule = defaultRules.vector[vectorRuleId];
var vectorState = ruleDefinition.ruleToVectorState(rule);
//console.log("Refreshing vectorPushRules for " + vectorRuleId +", "+ ruleDefinition.description +", " + rule +", " + vectorState);
self.state.vectorPushRules.push({
"vectorRuleId": vectorRuleId,
"description" : ruleDefinition.description,
"rule": rule,
"vectorState": vectorState,
});
// if there was a rule which we couldn't parse, add it to the external list
if (rule && !vectorState) {
rule.description = ruleDefinition.description;
self.state.externalPushRules.push(rule);
}
}
}
// 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"
};
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);
}
}
});
var pushersPromise = MatrixClientPeg.get().getPushers().then(function(resp) {
//console.log("resolving pushersPromise");
self.setState({pushers: resp.pushers});
});
q.all([pushRulesPromise, pushersPromise]).then(function() {
self.setState({
phase: self.phases.DISPLAY
});
}, function(error) {
self.setState({
phase: self.phases.ERROR
});
}).finally(() => {
// actually explicitly update our state having been deep-manipulating it
self.setState({
masterPushRule: self.state.masterPushRule,
vectorContentRules: self.state.vectorContentRules,
vectorPushRules: self.state.vectorPushRules,
externalContentRules: self.state.externalContentRules,
externalPushRules: self.state.externalPushRules,
});
}).done();
},
_updatePushRuleActions: function(rule, actions, enabled) {
var cli = MatrixClientPeg.get();
return cli.setPushRuleActions(
'global', rule.kind, rule.rule_id, actions
).then( function() {
// Then, if requested, enabled or disabled the rule
if (undefined != enabled) {
return cli.setPushRuleEnabled(
'global', rule.kind, rule.rule_id, enabled
);
}
});
},
renderNotifRulesTableRow: function(title, className, pushRuleVectorState) {
return (
<tr key={ className }>
<th>
{title}
</th>
<th>
<input className= {className + "-" + PushRuleVectorState.OFF}
type="radio"
checked={ pushRuleVectorState === PushRuleVectorState.OFF }
onChange={ this.onNotifStateButtonClicked } />
</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>
</tr>
);
},
renderNotifRulesTableRows: function() {
var rows = [];
for (var i in this.state.vectorPushRules) {
var rule = this.state.vectorPushRules[i];
//console.log("rendering: " + rule.description + ", " + rule.vectorRuleId + ", " + rule.vectorState);
rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState));
}
return rows;
},
emailNotificationsRow: function(address, label) {
return (<div className="mx_UserNotifSettings_tableRow">
<div className="mx_UserNotifSettings_inputCell">
<input id="enableEmailNotifications_{address}"
ref="enableEmailNotifications_{address}"
type="checkbox"
checked={ UserSettingsStore.hasEmailPusher(this.state.pushers, address) }
onChange={ this.onEnableEmailNotificationsChange.bind(this, address) }
/>
</div>
<div className="mx_UserNotifSettings_labelCell">
<label htmlFor="enableEmailNotifications_{address}">
{label}
</label>
</div>
</div>);
},
render: function() {
var self = this;
var spinner;
if (this.state.phase === this.phases.LOADING) {
var Loader = sdk.getComponent("elements.Spinner");
spinner = <Loader />;
}
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 for this account
</label>
</div>
</div>
);
}
// When enabled, the master rule inhibits all existing rules
// So do not show all notification settings
if (this.state.masterPushRule && this.state.masterPushRule.enabled) {
return (
<div>
{masterPushRuleDiv}
<div className="mx_UserSettings_notifTable">
All notifications are currently disabled for all targets.
</div>
</div>
);
}
var emailNotificationsRow;
if (this.props.threepids.filter(function(tp) {
if (tp.medium == "email") {
return true;
}
}).length == 0) {
emailNotificationsRow = <div>
Add an email address above to configure email notifications
</div>;
} else {
// This only supports the first email address in your profile for now
emailNotificationsRow = this.emailNotificationsRow(
this.props.threepids[0].address,
"Enable email notifications ("+this.props.threepids[0].address+")"
);
}
// 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 cant be displayed here: { externalKeyWords }</li>);
}
var devicesSection;
if (this.state.pushers === undefined) {
devicesSection = <div className="error">Unable to fetch notification target list</div>
} else if (this.state.pushers.length == 0) {
devicesSection = null;
} else {
// TODO: It would be great to be able to delete pushers from here too,
// and this wouldn't be hard to add.
var rows = [];
for (var i = 0; i < this.state.pushers.length; ++i) {
rows.push(<tr key={ i }>
<td>{this.state.pushers[i].app_display_name}</td>
<td>{this.state.pushers[i].device_display_name}</td>
</tr>);
}
devicesSection = (<table className="mx_UserSettings_devicesTable">
<tbody>
{rows}
</tbody>
</table>);
}
if (devicesSection) {
devicesSection = (<div>
<h3>Notification targets</h3>
{ devicesSection }
</div>);
}
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">
{ spinner }
<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>
<div className="mx_UserNotifSettings_tableRow">
<div className="mx_UserNotifSettings_inputCell">
<input id="enableDesktopAudioNotifications"
ref="enableDesktopAudioNotifications"
type="checkbox"
checked={ UserSettingsStore.getEnableAudioNotifications() }
onChange={ (e) => {
UserSettingsStore.setEnableAudioNotifications(e.target.checked);
this.forceUpdate();
}} />
</div>
<div className="mx_UserNotifSettings_labelCell">
<label htmlFor="enableDesktopAudioNotifications">
Enable audible notifications in web client
</label>
</div>
</div>
{ emailNotificationsRow }
<div className="mx_UserNotifSettings_pushRulesTableWrapper">
<table className="mx_UserNotifSettings_pushRulesTable">
<thead>
<tr>
<th width="55%"></th>
<th width="15%">Off</th>
<th width="15%">On</th>
<th width="15%">Highlight<br/>&amp; sound</th>
</tr>
</thead>
<tbody>
{ this.renderNotifRulesTableRows() }
</tbody>
</table>
</div>
{ advancedSettings }
{ devicesSection }
</div>
</div>
);
}
});

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_TextualEvent {
opacity: 0.5;
overflow-y: hidden;
}
'use strict';
module.exports = {
};

View File

@@ -0,0 +1,96 @@
/*
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";
}
}
};

View File

@@ -0,0 +1,182 @@
/*
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);
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);
}
},
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();
},
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}
/>
);
});
}
};

View File

@@ -0,0 +1,506 @@
/*
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);
}
},
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 tileTypes = {
'm.room.message': sdk.getComponent('molecules.MessageTile'),
'm.room.member' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.invite' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.answer' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.hangup' : sdk.getComponent('molecules.EventAsTextTile'),
'm.room.topic' : sdk.getComponent('molecules.EventAsTextTile'),
};
var DateSeparator = sdk.getComponent('molecules.DateSeparator');
var ret = [];
var count = 0;
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
var mxEv = this.state.room.timeline[i];
var TileType = tileTypes[mxEv.getType()];
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 = <DateSeparator key={ts1} ts={ts1}/>;
continuation = false;
}
if (!TileType) continue;
ret.unshift(
<li key={mxEv.getId()}><TileType 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,
});
}
}
};

View File

@@ -0,0 +1,58 @@
/*
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;

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -44,7 +44,6 @@ ConferenceCall.prototype.setup = function() {
// looking for a 1:1 room with this conf user ID!)
var call = Matrix.createNewMatrixCall(self.client, room.roomId);
call.confUserId = self.confUserId;
call.groupRoomId = self.groupRoomId;
return call;
});
};
@@ -86,15 +85,15 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
};
/**
* Check if this user ID is in fact a conference bot.
* @param {string} userId The user ID to check.
* Check if this room member is in fact a conference bot.
* @param {RoomMember} The room member to check
* @return {boolean} True if it is a conference bot.
*/
module.exports.isConferenceUser = function(userId) {
if (userId.indexOf("@" + USER_PREFIX) !== 0) {
module.exports.isConferenceUser = function(roomMember) {
if (roomMember.userId.indexOf("@" + USER_PREFIX) !== 0) {
return false;
}
var base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length);
var base64part = roomMember.userId.split(":")[0].substring(1 + USER_PREFIX.length);
if (base64part) {
var decoded = new Buffer(base64part, "base64").toString();
// ! $STUFF : $STUFF

View File

@@ -1,125 +0,0 @@
/*
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 PushRuleVectorState = require('./PushRuleVectorState');
module.exports = {
/**
* Extract the keyword rules from a list of rules, and parse them
* into a form which is useful for Vector's UI.
*
* Returns an object containing:
* rules: the primary list of keyword rules
* vectorState: a PushRuleVectorState indicating whether those rules are
* OFF/ON/LOUD
* externalRules: a list of other keyword rules, with states other than
* vectorState
*/
parseContentRules: function(rulesets) {
// first categorise the keyword rules in terms of their actions
var contentRules = this._categoriseContentRules(rulesets);
// 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 externalRules.
// 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) {
return {
vectorState: PushRuleVectorState.LOUD,
rules: contentRules.loud,
externalRules: [].concat(contentRules.loud_but_disabled, contentRules.on, contentRules.on_but_disabled, contentRules.other),
};
}
else if (contentRules.loud_but_disabled.length) {
return {
vectorState: PushRuleVectorState.OFF,
rules: contentRules.loud_but_disabled,
externalRules: [].concat(contentRules.on, contentRules.on_but_disabled, contentRules.other),
};
}
else if (contentRules.on.length) {
return {
vectorState: PushRuleVectorState.ON,
rules: contentRules.on,
externalRules: [].concat(contentRules.on_but_disabled, contentRules.other),
};
}
else if (contentRules.on_but_disabled.length) {
return {
vectorState: PushRuleVectorState.OFF,
rules: contentRules.on_but_disabled,
externalRules: contentRules.other,
}
} else {
return {
vectorState: PushRuleVectorState.ON,
rules: [],
externalRules: contentRules.other,
}
}
},
_categoriseContentRules: function(rulesets) {
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];
// check it's not a default rule
if (r.rule_id[0] === '.' || kind !== 'content') {
continue;
}
r.kind = kind; // is this needed? not sure
switch (PushRuleVectorState.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;
}
}
}
return contentRules;
},
};

View File

@@ -1,89 +0,0 @@
/*
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';
module.exports = {
// Encodes a dictionary of {
// "notify": true/false,
// "sound": string or undefined,
// "highlight: true/false,
// }
// to a list of push actions.
encodeActions: function(action) {
var notify = action.notify;
var sound = action.sound;
var highlight = action.highlight;
if (notify) {
var actions = ["notify"];
if (sound) {
actions.push({"set_tweak": "sound", "value": sound});
}
if (highlight) {
actions.push({"set_tweak": "highlight"});
} else {
actions.push({"set_tweak": "highlight", "value": false});
}
return actions;
} else {
return ["dont_notify"];
}
},
// Decode a list of actions to a dictionary of {
// "notify": true/false,
// "sound": string or undefined,
// "highlight: true/false,
// }
// If the actions couldn't be decoded then returns null.
decodeActions: function(actions) {
var notify = false;
var sound = null;
var highlight = false;
for (var i = 0; i < actions.length; ++i) {
var action = actions[i];
if (action === "notify") {
notify = true;
} else if (action === "dont_notify") {
notify = false;
} else if (typeof action === 'object') {
if (action.set_tweak === "sound") {
sound = action.value
} else if (action.set_tweak === "highlight") {
highlight = action.value;
} else {
// We don't understand this kind of tweak, so give up.
return null;
}
} else {
// We don't understand this kind of action, so give up.
return null;
}
}
if (highlight === undefined) {
// If a highlight tweak is missing a value then it defaults to true.
highlight = true;
}
var result = {notify: notify, highlight: highlight};
if (sound !== null) {
result.sound = sound;
}
return result;
},
};

View File

@@ -1,94 +0,0 @@
/*
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 StandardActions = require('./StandardActions');
var NotificationUtils = require('./NotificationUtils');
var states = {
/** The push rule is disabled */
OFF: "off",
/** 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",
};
module.exports = {
/**
* Enum for state of a push rule as defined by the Vector UI.
* @readonly
* @enum {string}
*/
states: states,
/**
* Convert a PushRuleVectorState to a list of actions
*
* @return [object] list of push-rule actions
*/
actionsFor: function(pushRuleVectorState) {
if (pushRuleVectorState === this.ON) {
return StandardActions.ACTION_NOTIFY;
}
else if (pushRuleVectorState === this.LOUD) {
return StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND;
}
},
/**
* Convert a pushrule's actions to a PushRuleVectorState.
*
* Determines whether a content rule is in the PushRuleVectorState.ON
* category or in PushRuleVectorState.LOUD, regardless of its enabled
* state. Returns null if it does not match these categories.
*/
contentRuleVectorStateKind: function(rule) {
var decoded = NotificationUtils.decodeActions(rule.actions);
if (!decoded) {
return null;
}
// Count tweaks to determine if it is a ON or LOUD rule
var tweaks = 0;
if (decoded.sound) {
tweaks++;
}
if (decoded.highlight) {
tweaks++;
}
var stateKind = null;
switch (tweaks) {
case 0:
stateKind = this.ON;
break;
case 2:
stateKind = this.LOUD;
break;
}
return stateKind;
},
};
for (var k in states) {
module.exports[k] = states[k];
};

View File

@@ -1,30 +0,0 @@
/*
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 NotificationUtils = require('./NotificationUtils');
var encodeActions = NotificationUtils.encodeActions;
module.exports = {
ACTION_NOTIFY: encodeActions({notify: true}),
ACTION_NOTIFY_DEFAULT_SOUND: encodeActions({notify: true, sound: "default"}),
ACTION_NOTIFY_RING_SOUND: encodeActions({notify: true, sound: "ring"}),
ACTION_HIGHLIGHT_DEFAULT_SOUND: encodeActions({notify: true, sound: "default", highlight: true}),
ACTION_DONT_NOTIFY: encodeActions({notify: false}),
ACTION_DISABLED: null,
};

View File

@@ -1,134 +0,0 @@
/*
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 StandardActions = require('./StandardActions');
var PushRuleVectorState = require('./PushRuleVectorState');
class VectorPushRuleDefinition {
constructor(opts) {
this.kind = opts.kind;
this.description = opts.description;
this.vectorStateToActions = opts.vectorStateToActions;
}
// Translate the rule actions and its enabled value into vector state
ruleToVectorState(rule) {
var enabled = false;
var actions = null;
if (rule) {
enabled = rule.enabled;
actions = rule.actions;
}
for (var stateKey in PushRuleVectorState.states) {
var state = PushRuleVectorState.states[stateKey];
var vectorStateToActions = this.vectorStateToActions[state];
if (!vectorStateToActions) {
// No defined actions means that this vector state expects a disabled (or absent) rule
if (!enabled) {
return state;
}
} else {
// The actions must match to the ones expected by vector state
if (enabled && JSON.stringify(rule.actions) === JSON.stringify(vectorStateToActions)) {
return state;
}
}
}
console.error("Cannot translate rule actions into Vector rule state. Rule: " +
JSON.stringify(rule));
return undefined;
}
};
/**
* The descriptions of rules managed by the Vector UI.
*/
module.exports = {
// Messages containing user's display name
// (skip contains_user_name which is too geeky)
".m.rule.contains_display_name": new VectorPushRuleDefinition({
kind: "underride",
description: "Messages containing my name",
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND,
off: StandardActions.ACTION_DISABLED
}
}),
// Messages just sent to the user in a 1:1 room
".m.rule.room_one_to_one": new VectorPushRuleDefinition({
kind: "underride",
description: "Messages in one-to-one chats",
vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_NOTIFY_DEFAULT_SOUND,
off: StandardActions.ACTION_DONT_NOTIFY
}
}),
// 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.
".m.rule.message": new VectorPushRuleDefinition({
kind: "underride",
description: "Messages in group chats",
vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_NOTIFY_DEFAULT_SOUND,
off: StandardActions.ACTION_DONT_NOTIFY
}
}),
// Invitation for the user
".m.rule.invite_for_me": new VectorPushRuleDefinition({
kind: "underride",
description: "When I'm invited to a room",
vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_NOTIFY_DEFAULT_SOUND,
off: StandardActions.ACTION_DISABLED
}
}),
// Incoming call
".m.rule.call": new VectorPushRuleDefinition({
kind: "underride",
description: "Call invitation",
vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_NOTIFY_RING_SOUND,
off: StandardActions.ACTION_DISABLED
}
}),
// Notifications from bots
".m.rule.suppress_notices": new VectorPushRuleDefinition({
kind: "override",
description: "Messages sent by bot",
vectorStateToActions: {
// .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
on: StandardActions.ACTION_DISABLED,
loud: StandardActions.ACTION_NOTIFY_DEFAULT_SOUND,
off: StandardActions.ACTION_DONT_NOTIFY,
}
}),
};

View File

@@ -1,24 +0,0 @@
/*
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';
module.exports = {
NotificationUtils: require('./NotificationUtils'),
PushRuleVectorState: require('./PushRuleVectorState'),
VectorPushRulesDefinitions: require('./VectorPushRulesDefinitions'),
ContentRules: require('./ContentRules'),
};

View File

@@ -0,0 +1,22 @@
/*
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_MemberAvatar {
z-index: 20;
border-radius: 20px;
background-color: #dbdbdb;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -22,13 +22,8 @@ html {
}
body {
/* 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;
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
font-size: 16px;
color: #454545;
border: 0px;
margin: 0px;
@@ -39,9 +34,9 @@ div.error {
}
h2 {
color: #454545;
color: #80cef4;
font-weight: 400;
font-size: 18px;
font-size: 20px;
margin-top: 16px;
margin-bottom: 16px;
}
@@ -49,35 +44,7 @@ h2 {
a:hover,
a:link,
a:visited {
color: #76cfa6;
}
input[type=text]:focus, textarea:focus {
border: 1px solid #76CFA6;
outline: none;
box-shadow: none;
}
/* applied to side-panels and messagepanel when in RoomSettings */
.mx_fadable {
opacity: 1;
-webkit-transition: opacity 0.2s ease-in-out;
-moz-transition: opacity 0.2s ease-in-out;
-ms-transition: opacity 0.2s ease-in-out;
-o-transition: opacity 0.2s ease-in-out;
}
/* 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;
}
/* hack to avoid accidental click-throughs if you miss the narrow scrollbar */
.gm-scrollbar.-vertical {
border-left: 6px solid transparent;
color: #80CEF4;
}
.mx_ContextualMenu_background {
@@ -91,7 +58,7 @@ input[type=text]:focus, textarea:focus {
}
.mx_ContextualMenu {
border: 1px solid #a4a4a4;
border: 1px solid #a9dbf4;
border-radius: 8px;
background-color: #fff;
color: #747474;
@@ -100,33 +67,32 @@ input[type=text]:focus, textarea:focus {
padding: 6px;
}
.mx_ContextualMenu_chevron_right {
.mx_ContextualMenu_chevron {
padding: 12px;
position: absolute;
right: -21px;
top: 0px;
}
.mx_ContextualMenu_chevron_left {
padding: 12px;
position: absolute;
left: -21px;
top: 0px;
}
.mx_ContextualMenu_field {
padding: 3px 6px 3px 6px;
cursor: pointer;
}
.mx_ContextualMenu_spinner {
display: block;
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%;
@@ -147,36 +113,18 @@ input[type=text]:focus, textarea:focus {
background-color: #fff;
color: #747474;
text-align: center;
z-index: 4010;
z-index: 2010;
font-weight: 300;
font-size: 15px;
font-size: 16px;
position: relative;
border-radius: 8px;
max-width: 80%;
max-width: 75%;
}
.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_ImageView {
margin: 6px;
/* hack: flexbox bug? */
margin-bottom: 4px;
}
.mx_Dialog_content {
@@ -187,54 +135,26 @@ input[type=text]:focus, textarea:focus {
padding-bottom: 24px;
}
.mx_Dialog button, .mx_Dialog input[type="submit"] {
.mx_Dialog button {
border: 0px;
height: 36px;
border-radius: 36px;
font-weight: 400;
font-size: 15px;
font-size: 16px;
color: #fff;
background-color: #76cfa6;
background-color: #80cef4;
margin-left: 8px;
margin-right: 8px;
padding-left: 1em;
padding-right: 1em;
}
.mx_Dialog_title {
.mx_ErrorDialogTitle,
.mx_QuestionDialogTitle {
min-height: 16px;
padding: 12px;
border-bottom: 1px solid #a4a4a4;
border-bottom: 1px solid #a9dbf4;
font-weight: bold;
font-size: 18px;
font-size: 20px;
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;
background-color: #fff;
}
.emojione {
height: 1em;
vertical-align: middle;
}
::-moz-selection {
background-color: #76CFA6;
color: white;
}
::selection {
background-color: #76CFA6;
color: white;
}

View File

@@ -1,3 +1,7 @@
.mx_RoomDropTarget,
.mx_RoomList_favourites_label,
.mx_RoomList_archive_label,
.mx_RoomHeader_search,
.mx_RoomSettings_encrypt,
.mx_CreateRoom_encrypt,
.mx_RightPanel_filebutton

View File

@@ -1,138 +0,0 @@
/*
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_RoomStatusBar {
margin-top: 15px;
margin-left: 65px;
min-height: 34px;
}
/* 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 span {
color: #4a4a4a;
opacity: 0.5;
position: relative;
top: -4px;
/*
animation-duration: 1s;
animation-name: bounce;
animation-direction: alternate;
animation-iteration-count: infinite;
*/
}
.mx_RoomStatusBar_placeholderIndicator span:nth-child(1) {
animation-delay: 0.3s;
}
.mx_RoomStatusBar_placeholderIndicator span:nth-child(2) {
animation-delay: 0.6s;
}
.mx_RoomStatusBar_placeholderIndicator span:nth-child(3) {
animation-delay: 0.9s;
}
@keyframes bounce {
from {
opacity: 0.5;
top: 0;
}
to {
opacity: 0.2;
top: -3px;
}
}
.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: 26px;
}
.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;
}

View File

@@ -1,68 +0,0 @@
/*
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_SearchBox {
height: 24px;
margin-left: 16px;
margin-right: 20px;
padding-top: 24px;
padding-bottom: 22px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
display: flex;
display: -webkit-flex;
}
.mx_SearchBox_searchButton {
margin-right: 10px;
pointer-events: none;
}
.mx_SearchBox_closeButton {
cursor: pointer;
}
.mx_SearchBox_search {
flex: 1 1 auto;
-webkit-flex: 1 1 auto;
width: 0px;
font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
font-size: 12px;
margin-top: -2px;
height: 24px;
border: 0px ! important;
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1) ! important; */
background-color: transparent;
border: 0px;
}
.mx_SearchBox_minimise,
.mx_SearchBox_maximise {
margin-top: 3px;
cursor: pointer;
}
.mx_SearchBox_minimise {
margin-left: 10px;
}
.mx_SearchBox_maximise {
margin-left: 9px;
}
.mx_SearchBox object {
pointer-events: none;
}

View File

@@ -1,61 +0,0 @@
/*
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_UploadBar {
position: relative;
}
.mx_UploadBar_uploadProgressOuter {
height: 5px;
margin-left: 63px;
margin-top: -1px;
padding-bottom: 5px;
}
.mx_UploadBar_uploadProgressInner {
background-color: #76cfa6;
height: 5px;
}
.mx_UploadBar_uploadFilename {
margin-top: 5px;
margin-left: 65px;
opacity: 0.5;
color: #4a4a4a;
}
.mx_UploadBar_uploadIcon {
float: left;
margin-top: 5px;
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;
}

View File

@@ -1,188 +0,0 @@
/*
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 70px;
flex: 0 0 70px;
}
.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;
margin-top: -20px;
overflow-y: auto;
}
.mx_UserSettings h3 {
clear: both;
margin-left: 63px;
text-transform: uppercase;
color: #3d3b39;
font-weight: 600;
font-size: 13px;
margin-top: 26px;
margin-bottom: 10px;
}
.mx_UserSettings_section h3 {
margin-left: 0px;
}
.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_section {
margin-left: 63px;
margin-top: 28px;
margin-bottom: 28px;
}
.mx_UserSettings_accountTable
.mx_UserSettings_notifTable
{
display: table;
}
.mx_UserSettings_notifTable .mx_Spinner {
position: absolute;
}
.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;
}

View File

@@ -1,24 +0,0 @@
/*
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;
background-color: #fff;
font-size: 15px;
}

View File

@@ -1,109 +0,0 @@
/*
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;
font-size: 14px;
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;
}

View File

@@ -1,214 +0,0 @@
/*
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;
cursor: pointer;
}
.mx_EventTile_continuation {
margin-top: 8px ! important;
}
.mx_EventTile .mx_SenderProfile {
color: #454545;
opacity: 0.5;
display: inline-block;
font-size: 13px;
overflow-y: hidden;
cursor: pointer;
}
.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;
}
/* all the overflow-y: hidden; are to trap Zalgos -
but they introduce an implicit overflow-x: auto.
so make that explicitly hidden too to avoid random
horizontal scrollbars occasionally appearing, like in
https://github.com/vector-im/vector-web/issues/1154
*/
.mx_EventTile_content {
display: block;
overflow-y: hidden;
overflow-x: 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;
}
/* have to use overlay rather than auto otherwise Linux and Windows
Chrome gets very confused about vertical spacing:
https://github.com/vector-im/vector-web/issues/754
*/
.mx_EventTile_content .markdown-body pre {
overflow-x: overlay;
overflow-y: visible;
}
.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_msgOption a {
text-decoration: none;
}
.mx_EventTile .mx_MessageTimestamp {
display: block;
visibility: hidden;
text-align: right;
white-space: nowrap;
}
.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;
}

View File

@@ -1,65 +0,0 @@
/*
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_LinkPreviewWidget {
margin-top: 15px;
margin-right: 15px;
margin-bottom: 15px;
display: -webkit-flex;
display: flex;
border-left: 4px solid #ddd;
color: #888;
}
.mx_LinkPreviewWidget_image {
-webkit-flex: 0 0 100px;
flex: 0 0 100px;
margin-left: 15px;
text-align: center;
cursor: pointer;
}
.mx_LinkPreviewWidget_caption {
margin-left: 15px;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
}
.mx_LinkPreviewWidget_title {
display: inline;
font-weight: bold;
}
.mx_LinkPreviewWidget_siteName {
display: inline;
}
.mx_LinkPreviewWidget_description {
margin-top: 8px;
white-space: normal;
word-break: break-word;
}
.mx_LinkPreviewWidget_cancel {
visibility: hidden;
cursor: pointer;
-webkit-flex: 0 0 40px;
flex: 0 0 40px;
}
.mx_LinkPreviewWidget:hover .mx_LinkPreviewWidget_cancel {
visibility: visible;
}

View File

@@ -1,74 +0,0 @@
/*
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 {
margin-top: 20px;
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_avatar .mx_BaseAvatar {
cursor: not-allowed;
}
.mx_MemberInfo_avatar .mx_BaseAvatar.mx_BaseAvatar_image {
cursor: pointer;
}
.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;
}

View File

@@ -1,103 +0,0 @@
/*
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%;
margin-top: 12px;
margin-right: 20px;
-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 .mx_Spinner {
flex: 0;
-webkit-flex: 0;
}
.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_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;
}

View File

@@ -1,263 +0,0 @@
/*
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: 70px;
-webkit-align-items: center;
align-items: center;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
.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_spinner {
height: 36px;
-webkit-box-ordinal-group: 2;
-moz-box-ordinal-group: 2;
-ms-flex-order: 2;
-webkit-order: 2;
order: 2;
padding-left: 12px;
padding-right: 12px;
}
.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: 70px;
color: #454545;
font-size: 22px;
font-weight: bold;
overflow: hidden;
margin-left: 63px;
text-overflow: ellipsis;
width: 100%;
}
.mx_RoomHeader_simpleHeader .mx_RoomHeader_cancelButton {
float: right;
}
.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;
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_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: #A2A2A2;
font-weight: 300;
font-size: 13px;
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_avatar .mx_BaseAvatar_image {
object-fit: cover;
}
.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;
}

View File

@@ -1,35 +0,0 @@
/*
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_RoomList {
padding-top: 8px;
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;
}

View File

@@ -1,68 +0,0 @@
/*
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_RoomPreviewBar {
text-align: center;
height: 176px;
-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;
}
.mx_RoomPreviewBar_wrapper {
}
.mx_RoomPreviewBar_invite_text {
color: #454545;
}
.mx_RoomPreviewBar_join_text {
color: #ff0064;
}
.mx_RoomPreviewBar_preview_text {
margin-top: 25px;
color: #a4a4a4;
}
.mx_RoomPreviewBar_join_text a {
text-decoration: underline;
cursor: pointer;
}
.mx_RoomPreviewBar_warning {
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
padding: 8px;
}
.mx_RoomPreviewBar_warningIcon {
padding: 12px;
}

View File

@@ -1,187 +0,0 @@
/*
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 .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_warning {
color: #ff0064;
font-weight: bold;
margin-top: 8px;
margin-bottom: 8px;
}
.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;
}

View File

@@ -1,132 +0,0 @@
/*
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_RoomTile {
cursor: pointer;
/* This fixes wrapping of long room names, but breaks drag & drop previews */
/* display: table-row; */
font-size: 13px;
position: relative;
}
.mx_RoomTile_avatar {
display: table-cell;
padding-right: 11px;
padding-top: 6px;
padding-bottom: 6px;
padding-left: 18px;
width: 24px;
height: 24px;
position: relative;
vertical-align: middle;
}
.mx_RoomTile_name {
display: table-cell;
width: 100%;
vertical-align: middle;
overflow: hidden;
word-break: break-word;
padding-right: 15px;
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 {
display: none;
}
.collapsed .mx_RoomTile_badge {
margin-top: -15px;
right: 7px;
}
.mx_RoomTile_badge {
min-width: 12px;
height: 16px;
position: absolute;
right: 16px;
top: 50%;
margin-top: -8px;
border-radius: 16px;
color: #fff;
font-weight: bold;
font-size: 11px;
text-align: center;
padding: 0px 4px 0px 4px;
}
.mx_RoomTile_unreadNotify .mx_RoomTile_badge {
background-color: #76cfa6;
}
.mx_RoomTile_highlight .mx_RoomTile_badge {
background-color: #ff0064;
}
.mx_RoomTile_unread,
.mx_RoomTile_highlight {
font-weight: bold;
}
/* the inner highlight span has 4px of padding on it, so we shrink the _avatar by 4px and grow the _name by 4px to fit it in */
.mx_RoomTile_selected .mx_RoomTile_name {
padding-right: 19px;
}
.mx_RoomTile_selected .mx_RoomTile_avatar {
padding-right: 7px;
}
/* leave room for the badge, if present.
N.B. this has to come after the above _selected width tweaks */
.mx_RoomTile_unreadNotify .mx_RoomTile_name,
.mx_RoomTile_highlight .mx_RoomTile_name {
padding-right: 40px;
}
.mx_RoomTile_selected .mx_RoomTile_name span {
display: inline-block;
position: relative;
width: 100%;
padding: 4px;
margin-top: -4px;
margin-bottom: -4px;
border-radius: 2px;
background-color: rgba(118,207,166,0.2);
}
/* stop the span from overlapping with the badge */
.mx_RoomTile_unreadNotify.mx_RoomTile_selected .mx_RoomTile_name span,
.mx_RoomTile_highlight.mx_RoomTile_selected .mx_RoomTile_name span {
padding-right: 22px;
}
.mx_RoomTile_arrow {
position: absolute;
right: 0px;
}
.mx_RoomTile:hover {
}

View File

@@ -1,81 +0,0 @@
/*
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;
background-color: #fff;
margin-left: 3px;
font-size: 15px;
margin-bottom: 8px;
width: 189px;
}
.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;
}

View File

@@ -1,54 +0,0 @@
/*
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;
margin-bottom: 2px;
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;
vertical-align: initial;
color: #fff;
}
.mx_TabCompleteBar_item img {
margin-right: 8px;
vertical-align: middle;
}
.mx_TabCompleteBar_text {
color: #4a4a4a;
vertical-align: middle;
opacity: 0.5;
}

View File

@@ -1,45 +0,0 @@
/*
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 #e5e5e5;
}
.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;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,12 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MImageBody {
display: block;
}
.mx_MImageBody_thumbnail {
max-width: 100%;
.mx_MImageTile_thumbnail {
/*
background-color: #fff;
border: 2px solid #fff;
@@ -27,20 +22,16 @@ limitations under the License.
*/
}
.mx_MImageBody_download {
color: #76cfa6;
.mx_MImageTile_download {
color: #80cef4;
cursor: pointer;
}
.mx_MImageBody_download a {
color: #76cfa6;
.mx_MImageTile_download a {
color: #80cef4;
text-decoration: none;
}
.mx_MImageBody_download object {
margin-left: -16px;
padding-right: 4px;
margin-top: -4px;
vertical-align: middle;
pointer-events: none;
}
.mx_MImageTile_download img {
padding-right: 8px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2016 OpenMarket Ltd
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.
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_PresenceLabel {
font-size: 11px;
.mx_MNoticeTile {
opacity: 0.5;
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MTextBody {
.mx_MTextTile {
white-space: pre-wrap;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_BaseAvatar {
position: relative;
}
.mx_BaseAvatar_initial {
position: absolute;
z-index: 1;
color: #fff;
.mx_MatrixToolbar {
text-align: center;
speak: none;
pointer-events: none;
font-weight: normal;
background-color: #ff0064;
color: #fff;
font-weight: bold;
padding: 6px;
}
.mx_BaseAvatar_image {
border-radius: 40px;
vertical-align: top;
.mx_MatrixToolbar button {
margin-left: 12px;
}
.mx_MatrixToolbar_close {
float: right;
margin-top: 3px;
margin-right: 12px;
cursor: pointer;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,7 +14,3 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MNoticeBody {
white-space: pre-wrap;
opacity: 0.6;
}

View File

@@ -0,0 +1,134 @@
/*
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;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -15,107 +15,70 @@ limitations under the License.
*/
.mx_MessageComposer_wrapper {
max-width: 960px;
max-width: 720px;
height: 50px;
vertical-align: middle;
margin: auto;
border-top: 1px solid #e5e5e5;
background-color: #fff;
border-radius: 25px;
border: 1px solid #a9dbf4;
}
.mx_MessageComposer_row {
display: table-row;
width: 100%;
height: 50px;
}
.mx_MessageComposer .mx_MessageComposer_avatar {
display: table-cell;
padding-left: 10px;
padding-right: 28px;
vertical-align: middle;
padding-left: 5px;
padding-right: 10px;
height: 50px;
}
.mx_MessageComposer .mx_MessageComposer_avatar .mx_BaseAvatar {
display: block;
}
.mx_MessageComposer_composecontrols {
width: 100%;
}
.mx_MessageComposer_noperm_error {
display: table-cell;
width: 100%;
vertical-align: middle;
height: 60px;
text-align: center;
font-style: italic;
color: #888;
.mx_MessageComposer .mx_MessageComposer_avatar img {
margin-top: 5px;
border-radius: 20px;
background-color: #dbdbdb;
}
.mx_MessageComposer_input {
display: table-cell;
width: 100%;
vertical-align: middle;
height: 60px;
height: 50px;
}
.mx_MessageComposer_input textarea {
display: block;
font-size: 15px;
width: 100%;
padding: 0px;
margin-top: 6px;
margin-bottom: 6px;
height: 1.2em;
padding-top: 0.7em;
padding-bottom: 0.7em;
border: 0px;
resize: none;
outline: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
color: #454545;
background-color: #fff;
box-shadow: none;
/* needed for FF */
font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
font-family: 'Lato', Helvetica, Arial, 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;
opacity: 1.0;
}
.mx_MessageComposer_input textarea::-webkit-input-placeholder {
color: #76cfa6;
}
.mx_MessageComposer_upload,
.mx_MessageComposer_hangup,
.mx_MessageComposer_voicecall,
.mx_MessageComposer_videocall {
.mx_MessageComposer_upload {
display: table-cell;
vertical-align: middle;
padding-left: 10px;
padding-right: 10px;
padding-right: 15px;
cursor: pointer;
}
.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 {
.mx_MessageComposer_upload img {
margin-top: 5px;
}

View File

@@ -0,0 +1,111 @@
/*
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_MessageTile {
max-width: 100%;
clear: both;
margin-top: 32px;
margin-left: 56px;
}
.mx_MessageTile_avatar {
padding-left: 12px;
padding-right: 12px;
margin-left: -64px;
margin-top: -7px;
float: left;
}
.mx_MessageTile_avatar img {
background-color: #dbdbdb;
border-radius: 20px;
border: 0px;
}
.mx_MessageTile_continuation {
margin-top: 8px ! important;
}
.mx_MessageTile .mx_SenderProfile {
color: #454545;
opacity: 0.5;
font-size: 14px;
margin-bottom: 4px;
display: block;
}
.mx_MessageTile .mx_MessageTimestamp {
color: #454545;
opacity: 0.5;
font-size: 14px;
float: right;
}
.mx_MessageTile_content {
padding-right: 100px;
display: block;
}
.mx_MessageTile_notice .mx_MessageTile_content {
opacity: 0.5;
}
.mx_MessageTile_sending {
color: #ddd;
}
.mx_MessageTile_notSent {
color: #f11;
}
.mx_MessageTile_highlight {
color: #FF0064;
}
.mx_MessageTile_msgOption {
float: right;
}
.mx_MessageTimestamp {
display: none;
}
.mx_MessageTile_last .mx_MessageTimestamp {
display: block;
}
.mx_MessageTile:hover .mx_MessageTimestamp {
display: block;
}
.mx_MessageTile_editButton {
float: right;
display: none;
border: 0px;
outline: none;
}
.mx_MessageTile:hover .mx_MessageTile_editButton {
display: inline-block;
}
.mx_MessageTile.menu .mx_MessageTile_editButton {
display: inline-block;
}
.mx_MessageTile.menu .mx_MessageTimestamp {
display: inline-block;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.

View File

@@ -0,0 +1,27 @@
/*
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_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;
}

View File

@@ -0,0 +1,173 @@
/*
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;
}

View File

@@ -0,0 +1,70 @@
/*
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;
}

View File

@@ -0,0 +1,104 @@
/*
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_RoomTile {
cursor: pointer;
display: table-row;
color: #818794;
}
.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;
position: relative;
}
.mx_RoomTile_avatar img {
border-radius: 20px;
background-color: #dbdbdb;
}
.mx_RoomTile_name {
display: table-cell;
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 16px;
}
.collapsed .mx_RoomTile_name {
display: none;
}
/*
.mx_RoomTile_nameBadge {
display: table;
width: 100%;
height: 50px;
}
.mx_RoomTile_badgeCell {
display: table-cell;
vertical-align: middle;
width: 26px;
}
.mx_RoomTile_badge {
background-color: #80cef4;
color: #fff;
border-radius: 26px;
font-weight: 400;
font-size: 14px;
line-height: 28px;
width: 26px;
height: 26px;
text-align: center;
}
*/
.mx_RoomTile_badge {
background-color: #ff0064;
border: 3px solid #fff;
border-radius: 16px;
width: 9px;
height: 9px;
position: absolute;
right: 9px;
bottom: 3px;
}
.mx_RoomTile_unread,
.mx_RoomTile_highlight,
.mx_RoomTile_invited
{
font-weight: bold;
color: #000;
}
.mx_RoomTile_selected {
background-color: #f3f8fa;
color: #80cef4;
font-weight: bold;
}
.mx_RoomTile:hover {
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -17,10 +17,11 @@ limitations under the License.
.mx_RoomTooltip {
display: none;
position: fixed;
border: 1px solid #a4a4a4;
border: 1px solid #a9dbf4;
border-radius: 8px;
background-color: #fff;
z-index: 1000;
margin-top: 6px;
left: 64px;
padding: 6px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -13,3 +13,8 @@ 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;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -25,7 +25,7 @@ limitations under the License.
.mx_ServerConfig_help:link {
opacity: 0.8;
font-size: 13px;
font-size: 14px;
font-weight: 300;
color: #4a4a4a;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -16,15 +16,14 @@ limitations under the License.
.mx_IncomingCallBox {
text-align: center;
border: 1px solid #a4a4a4;
border: 1px solid #a9dbf4;
border-radius: 8px;
background-color: #fff;
position: fixed;
position: absolute;
z-index: 1000;
left: 235px;
top: 155px;
padding: 6px;
margin-top: -3px;
margin-left: -20px;
width: 200px;
}
.mx_IncomingCallBox_chevron {
@@ -40,15 +39,14 @@ limitations under the License.
}
.mx_IncomingCallBox_buttons {
display: flex;
display: -webkit-flex;
display: table-row;
}
.mx_IncomingCallBox_buttons_cell {
vertical-align: middle;
display: table-cell;
padding: 6px;
flex: 1;
-webkit-flex: 1;
width: 50%;
}
.mx_IncomingCallBox_buttons_decline,
@@ -59,7 +57,6 @@ limitations under the License.
line-height: 36px;
border-radius: 36px;
color: #fff;
margin: auto;
}
.mx_IncomingCallBox_buttons_decline {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -31,15 +31,9 @@ limitations under the License.
}
.mx_VideoView_localVideoFeed {
width: 25%;
height: 25%;
width: 20%;
position: absolute;
left: 10px;
bottom: 10px;
left: 16px;
bottom: 28px;
z-index: 100;
}
.mx_VideoView_localVideoFeed video {
width: auto;
height: 100%;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -15,10 +15,10 @@ limitations under the License.
*/
.mx_CreateRoom {
width: 960px;
width: 720px;
margin-left: auto;
margin-right: auto;
color: #4a4a4a;
color: #4a4a4a;
}
.mx_CreateRoom input,
@@ -26,7 +26,7 @@ limitations under the License.
border-radius: 3px;
border: 1px solid #c7c7c7;
font-weight: 300;
font-size: 13px;
font-size: 14px;
padding: 9px;
margin-top: 6px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -34,21 +34,16 @@ limitations under the License.
cursor: pointer;
}
.mx_LeftPanel_callView {
}
.mx_LeftPanel .mx_RoomList_scrollbar {
.mx_LeftPanel .mx_RoomList {
-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 {
@@ -58,40 +53,17 @@ limitations under the License.
-webkit-order: 3;
order: 3;
border-top: 1px solid rgba(0, 0, 0, 0.1);
margin-left: 18px;
margin-right: 18px;
-webkit-flex: 0 0 60px;
flex: 0 0 60px;
-webkit-flex: 0 0 170px;
flex: 0 0 170px;
border-top: 1px solid #f3f8fa;
}
.mx_LeftPanel .mx_BottomLeftMenu_options {
margin-top: 18px;
.mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile {
color: #378bb4;
}
.mx_BottomLeftMenu_options object {
pointer-events: none;
}
.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
.mx_LeftPanel .mx_BottomLeftMenu_directory,
.mx_LeftPanel .mx_BottomLeftMenu_settings {
display: inline-block;
cursor: pointer;
}
.collapsed .mx_BottomLeftMenu_createRoom,
.collapsed .mx_BottomLeftMenu_directory,
.collapsed .mx_BottomLeftMenu_settings {
margin-left: 0px ! important;
padding-top: 3px ! important;
padding-bottom: 3px ! important;
}
.mx_LeftPanel .mx_BottomLeftMenu_directory {
margin-left: 10px;
}
.mx_LeftPanel .mx_BottomLeftMenu_settings {
float: right;
}
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
margin-top: 12px;
width: 100%;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,41 +14,47 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MatrixToolbar {
background-color: #76cfa6;
color: #fff;
.mx_MemberList {
height: 100%;
margin-bottom: 100px;
padding: 8px;
-webkit-flex: 1;
flex: 1;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
flex-direction: column;
-webkit-flex-direction: column;
}
.mx_MatrixToolbar_warning {
margin-left: 16px;
margin-right: 8px;
margin-top: -2px;
.mx_MemberList_chevron {
position: absolute;
right: 35px;
margin-top: -15px;
}
.mx_MatrixToolbar_link
{
color: #fff ! important;
text-decoration: underline ! important;
cursor: pointer;
.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_MatrixToolbar_close {
-webkit-flex: 1;
flex: 1;
cursor: pointer;
text-align: right;
.mx_MemberList_wrapper {
display: table;
table-layout: fixed;
width: 100%;
}
.mx_MatrixToolbar_close img {
display: block;
float: right;
margin-right: 10px;
.mx_MemberList h2 {
margin: 14px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -33,80 +33,35 @@ limitations under the License.
-webkit-order: 1;
order: 1;
border-bottom: 1px solid #e5e5e5;
margin-right: 20px;
-webkit-flex: 0 0 70px;
flex: 0 0 70px;
-webkit-flex: 0 0 66px;
flex: 0 0 66px;
}
/** Fixme - factor this out with the main header **/
.mx_RightPanel_headerButtonGroup {
margin-top: 25px;
float: left;
margin-top: 18px;
height: 48px;
float: right;
background-color: #fff;
margin-left: -4px;
border-radius: 48px;
border: 1px solid #a9dbf4;
margin-right: 22px;
}
.mx_RightPanel_headerButton {
cursor: pointer;
height: 48px;
display: table-cell;
vertical-align: middle;
padding-left: 15px;
padding-right: 15px;
position: relative;
vertical-align: middle;
padding-left: 8px;
padding-right: 8px;
}
.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,
.mx_RightPanel_blank {
.mx_RightPanel .mx_MemberList {
-webkit-box-ordinal-group: 2;
-moz-box-ordinal-group: 2;
-ms-flex-order: 2;
-webkit-order: 2;
order: 2;
flex: 1;
-webkit-flex: 1;
}
.mx_RightPanel_footer {
-webkit-box-ordinal-group: 3;
-moz-box-ordinal-group: 3;
-ms-flex-order: 3;
-webkit-order: 3;
order: 3;
border-top: 1px solid #e5e5e5;
margin-right: 20px;
-webkit-flex: 0 0 60px;
flex: 0 0 60px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -15,8 +15,7 @@ limitations under the License.
*/
.mx_RoomDirectory {
max-width: 960px;
width: 100%;
width: 720px;
margin-left: auto;
margin-right: auto;
margin-bottom: 12px;
@@ -26,21 +25,21 @@ limitations under the License.
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
display: flex;
flex-direction: column;
-webkit-flex-direction: column;
}
.mx_RoomDirectory_list {
-webkit-flex: 1;
-webkit-flex: 1;
flex: 1;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
display: flex;
flex-direction: column;
-webkit-flex-direction: column;
@@ -51,7 +50,7 @@ limitations under the License.
border-radius: 3px;
border: 1px solid #c7c7c7;
font-weight: 300;
font-size: 13px;
font-size: 14px;
padding: 9px;
margin-top: 12px;
margin-bottom: 12px;
@@ -59,67 +58,42 @@ limitations under the License.
.mx_RoomDirectory_tableWrapper {
overflow-y: auto;
-webkit-flex: 1 1 0;
-webkit-flex: 1 1 0;
flex: 1 1 0;
}
.mx_RoomDirectory_table {
font-size: 14px;
color: #4a4a4a;
width: 100%;
text-align: left;
table-layout: fixed;
}
.mx_RoomDirectory_roomAvatar {
width: 24px;
padding-left: 12px;
padding-right: 24px;
vertical-align: top;
}
.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 {
.mx_RoomDirectory_table th {
font-weight: 400;
font-size: 12px;
color: #b3b3b3;
}
.mx_RoomDirectory_roomMemberCount {
text-align: right;
width: 100px;
padding-right: 10px;
}
.mx_RoomDirectory_table tr {
padding-bottom: 10px;
.mx_RoomDirectory_table tbody {
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;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,10 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_ViewSource pre {
text-align: left;
font-size: 12px;
padding: 0.5em 1em 0.5em 1em;
word-wrap: break-word;
white-space: pre-wrap;
.mx_RoomList {
}
.mx_RoomList_recents {
margin-top: -12px;
display: table;
table-layout: fixed;
width: 100%;
}
.mx_RoomList h2 {
padding-left: 16px;
padding-right: 16px;
padding-bottom: 10px;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -36,15 +36,15 @@ limitations under the License.
-webkit-order: 1;
order: 1;
-webkit-flex: 0 0 70px;
flex: 0 0 70px;
-webkit-flex: 0 0 88px;
flex: 0 0 88px;
}
.mx_RoomView_fileDropTarget {
min-width: 0px;
max-width: 960px;
max-width: 720px;
width: 100%;
font-size: 18px;
font-size: 20px;
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 #e1dddd solid;
border: 2px dashed #80cef4;
border-bottom: none;
position: absolute;
top: 70px;
top: 88px;
bottom: 0px;
z-index: 3000;
}
@@ -84,31 +84,23 @@ limitations under the License.
order: 2;
min-width: 0px;
max-width: 960px;
max-width: 720px;
width: 100%;
margin: auto;
overflow: auto;
border-bottom: 1px solid #e5e5e5;
border-bottom: 1px solid #a8dbf3;
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
.mx_RoomView_topUnreadMessagesBar {
.mx_RoomView_messagePanel {
-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;
@@ -119,27 +111,8 @@ limitations under the License.
}
.mx_RoomView_messageListWrapper {
max-width: 960px;
max-width: 720px;
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_searchResultsPanel .mx_RoomView_messageListWrapper {
justify-content: flex-start;
-webkit-justify-content: flex-start;
}
.mx_RoomView_MessageList {
@@ -156,9 +129,8 @@ limitations under the License.
clear: both;
margin-top: 32px;
margin-bottom: 8px;
margin-left: 63px;
padding-bottom: 6px;
border-bottom: 1px solid #e5e5e5;
border-bottom: 1px solid #a8dbf3;
}
.mx_RoomView_invitePrompt {
@@ -169,7 +141,7 @@ limitations under the License.
order: 2;
min-width: 0px;
max-width: 960px;
max-width: 720px;
width: 100%;
margin: auto;
@@ -177,22 +149,55 @@ 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;
@@ -200,71 +205,51 @@ hr.mx_RoomView_myReadMarker {
order: 5;
width: 100%;
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
.mx_RoomView_statusAreaBox {
max-width: 960px;
margin: auto;
min-height: 60px;
}
.mx_RoomView_statusAreaBox_line {
margin-left: 65px;
border-top: 1px solid #e5e5e5;
height: 1px;
}
.mx_RoomView_inCall .mx_RoomView_statusAreaBox_line {
border-top: 1px hidden;
}
.mx_RoomView_inCall .mx_MessageComposer_wrapper {
border-top: 2px hidden;
}
.mx_RoomView_inCall .mx_RoomView_statusAreaBox {
background-color: #76CFA6;
color: #fff;
position: relative;
}
.mx_RoomView_voipChevron {
position: absolute;
bottom: -11px;
right: 11px;
}
.mx_RoomView_voipButton {
float: right;
margin-right: 13px;
cursor: pointer;
}
.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;
-webkit-flex: 0 0 63px;
flex: 0 0 63px;
margin-right: 2px;
}
.mx_RoomView_uploadProgressOuter {
width: 100%;
background-color: rgba(169, 219, 244, 0.5);
height: 4px;
}
.mx_RoomView_uploadProgressInner {
background-color: #80cef4;
height: 4px;
}
.mx_RoomView_uploadFilename {
margin-top: 15px;
margin-left: 56px;
}
.mx_RoomView_uploadIcon {
float: left;
margin-top: 6px;
margin-left: 5px;
}
.mx_RoomView_uploadCancel {
float: right;
margin-top: 6px;
margin-right: 10px;
}
.mx_RoomView_uploadBytes {
float: right;
opacity: 0.5;
margin-top: 15px;
margin-right: 10px;
}
.mx_RoomView_ongoingConfCallNotification {
width: 100%;
text-align: center;
background-color: #ff0064;
color: #fff;
font-weight: bold;
padding: 6px 0;
cursor: pointer;
}
padding: 6px;
}

View File

@@ -0,0 +1,21 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_UserSettings {
width: 720px;
margin-left: auto;
margin-right: auto;
}

View File

@@ -0,0 +1,3 @@
.mx_ViewSource pre {
text-align: left;
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -14,24 +14,12 @@ 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;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
display: flex;
flex-direction: column;
-webkit-flex-direction: column;
@@ -47,17 +35,7 @@ limitations under the License.
-webkit-order: 1;
order: 1;
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;
height: 21px;
}
.mx_MatrixChat_toolbarShowing {
@@ -91,10 +69,8 @@ limitations under the License.
-webkit-order: 1;
order: 1;
background-color: #eaf5f0;
-webkit-flex: 0 0 210px;
flex: 0 0 210px;
-webkit-flex: 0 0 230px;
flex: 0 0 230px;
}
.mx_MatrixChat .mx_LeftPanel.collapsed {
@@ -109,25 +85,17 @@ limitations under the License.
-webkit-order: 2;
order: 2;
padding-left: 25px;
padding-right: 22px;
background-color: #fff;
padding-left: 12px;
padding-right: 12px;
background-color: #f3f8fa;
-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. Height has to be auto here
* for RoomView to correctly fit when the Toolbar is shown.
* parents are both auto.
* Ideally we'd launch straight into the RoomView at this
* point, but instead we fudge it and make the middlePanel
* flex itself.
@@ -146,8 +114,9 @@ limitations under the License.
-webkit-order: 3;
order: 3;
-webkit-flex: 0 0 235px;
flex: 0 0 235px;
background-color: #f3f8fa;
-webkit-flex: 0 0 230px;
flex: 0 0 230px;
}
.mx_MatrixChat .mx_RightPanel.collapsed {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@@ -17,18 +17,6 @@ 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 {
@@ -40,10 +28,8 @@ 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 {
@@ -55,19 +41,11 @@ limitations under the License.
border-radius: 3px;
border: 1px solid #c7c7c7;
font-weight: 300;
font-size: 13px;
font-size: 14px;
padding: 9px;
margin-bottom: 14px;
}
.mx_Login_fieldLabel {
margin-top: -10px;
margin-left: 8px;
margin-bottom: 14px;
font-size: 13px;
opacity: 0.8;
}
.mx_Login_submit {
margin-top: 35px;
margin-bottom: 24px;
@@ -76,17 +54,16 @@ limitations under the License.
height: 40px;
border: 0px;
background-color: #76cfa6;
font-size: 15px;
font-size: 16px;
color: #fff;
}
.mx_Login_label {
font-size: 13px;
font-size: 14px;
opacity: 0.8;
}
.mx_Login_checkbox,
.mx_Login_radio {
.mx_Login_checkbox {
margin-right: 10px;
}
@@ -94,7 +71,7 @@ limitations under the License.
display: block;
text-align: center;
width: 100%;
font-size: 13px;
font-size: 14px;
opacity: 0.8;
}
@@ -105,9 +82,8 @@ limitations under the License.
.mx_Login_links {
display: block;
text-align: center;
margin-top: 15px;
width: 100%;
font-size: 13px;
font-size: 14px;
opacity: 0.8;
}
@@ -115,21 +91,6 @@ limitations under the License.
color: #4a4a4a;
}
.mx_Login_prompt {
padding-top: 15px;
padding-bottom: 15px;
font-size: 13px;
}
.mx_Login_forgot {
font-size: 13px;
opacity: 0.8;
}
.mx_Login_forgot:link {
color: #4a4a4a;
}
.mx_Login_loader {
position: absolute;
left: 50%;

View File

@@ -1,19 +0,0 @@
.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;
}

View File

@@ -1,42 +0,0 @@
/*
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;
cursor: pointer;
}
.mx_RoomSubList_chevron {
padding-left: 4px;
pointer-events: none;
}
.collapsed .mx_RoomSubList_chevron {
padding-left: 12px;
}

View File

@@ -1,152 +0,0 @@
/*
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: 30px;
padding-right: 30px;
min-height: 100%;
max-width: 240px;
color: #fff;
}
.mx_ImageView_cancel {
position: absolute;
top: 0px;
right: 0px;
padding: 35px;
cursor: pointer;
}
.mx_ImageView_name {
font-size: 18px;
margin-bottom: 6px;
word-wrap: break-word;
}
.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;
*/
}

View File

@@ -1,32 +0,0 @@
/*
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_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_MatrixChat_middlePanel .mx_Spinner {
height: auto;
}

View File

@@ -1,41 +0,0 @@
/*
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;
}

Some files were not shown because too many files have changed in this diff Show More