mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-07 01:21:02 +00:00
Compare commits
1 Commits
v0.7.5-r3
...
kegan/time
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80277583db |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,12 +1,3 @@
|
||||
/cert.pem
|
||||
/karma-reports
|
||||
/key.pem
|
||||
/lib
|
||||
/node_modules
|
||||
/packages/
|
||||
/vector/bundle.*
|
||||
/vector/components.css
|
||||
/vector/emojione/
|
||||
/vector/config.json
|
||||
/vector/olm.js
|
||||
.DS_Store
|
||||
node_modules
|
||||
vector/bundle.*
|
||||
lib
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 6 # node v6, to match jenkins
|
||||
install:
|
||||
- npm install
|
||||
- (cd node_modules/matrix-react-sdk && npm run build)
|
||||
@@ -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
|
||||
|
||||
329
CHANGELOG.md
329
CHANGELOG.md
@@ -1,329 +0,0 @@
|
||||
Changes in [0.7.5-r3](https://github.com/vector-im/vector-web/releases/tag/v0.7.5-r3) (2016-09-02)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.5-r2...v0.7.5-r3)
|
||||
|
||||
* Bump to matrix-react-sdk 0.6.5-r3 in order to fix bug #2020 (tightloop when flooded with join events)
|
||||
|
||||
|
||||
Changes in [0.7.5-r2](https://github.com/vector-im/vector-web/releases/tag/v0.7.5-r2) (2016-09-01)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.5-r1...v0.7.5-r2)
|
||||
|
||||
* Bump to matrix-react-sdk 0.6.5-r1 in order to fix guest access
|
||||
|
||||
Changes in [0.7.5-r1](https://github.com/vector-im/vector-web/releases/tag/v0.7.5-r1) (2016-08-28)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.5...v0.7.5-r1)
|
||||
|
||||
* Correctly pin deps :(
|
||||
|
||||
Changes in [0.7.5](https://github.com/vector-im/vector-web/releases/tag/v0.7.5) (2016-08-28)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.4-r1...v0.7.5)
|
||||
|
||||
* re-add leave button in RoomSettings
|
||||
* add /user URLs
|
||||
* recognise matrix.to links and other vector links
|
||||
* fix linkify dependency
|
||||
* fix avatar clicking in MemberInfo
|
||||
* fix RoomTagContextMenu so it works on historical rooms
|
||||
* warn people to put their Matrix HS on a separate domain to Vector
|
||||
* fix zalgos again
|
||||
* Add .travis.yml
|
||||
[\#2007](https://github.com/vector-im/vector-web/pull/2007)
|
||||
* add fancy changelog dialog
|
||||
[\#1972](https://github.com/vector-im/vector-web/pull/1972)
|
||||
* Update autocomplete design
|
||||
[\#1978](https://github.com/vector-im/vector-web/pull/1978)
|
||||
* Update encryption info in README
|
||||
[\#2001](https://github.com/vector-im/vector-web/pull/2001)
|
||||
* Added event/info message avatars back in
|
||||
[\#2000](https://github.com/vector-im/vector-web/pull/2000)
|
||||
* Wmwragg/chat message presentation
|
||||
[\#1987](https://github.com/vector-im/vector-web/pull/1987)
|
||||
* Make the notification slider work
|
||||
[\#1982](https://github.com/vector-im/vector-web/pull/1982)
|
||||
* Use cpx to copy olm.js, and add watcher
|
||||
[\#1966](https://github.com/vector-im/vector-web/pull/1966)
|
||||
* Make up a device display name
|
||||
[\#1959](https://github.com/vector-im/vector-web/pull/1959)
|
||||
|
||||
Changes in [0.7.4-r1](https://github.com/vector-im/vector-web/releases/tag/v0.7.4-r1) (2016-08-12)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.4...v0.7.4-r1)
|
||||
* Update to matrix-react-sdk 0.6.4-r1 to fix inviting multiple people
|
||||
|
||||
|
||||
Changes in [0.7.4](https://github.com/vector-im/vector-web/releases/tag/v0.7.4) (2016-08-11)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.3...v0.7.4)
|
||||
|
||||
* Don't show border on composer when not in RTE mode
|
||||
[\#1954](https://github.com/vector-im/vector-web/pull/1954)
|
||||
* Wmwragg/room tag menu
|
||||
[\#1941](https://github.com/vector-im/vector-web/pull/1941)
|
||||
* Don't redirect to mobile app if verifying 3pid
|
||||
[\#1951](https://github.com/vector-im/vector-web/pull/1951)
|
||||
* Make sure that we clear localstorage before *all* tests
|
||||
[\#1950](https://github.com/vector-im/vector-web/pull/1950)
|
||||
* Basic CSS for multi-invite dialog
|
||||
[\#1942](https://github.com/vector-im/vector-web/pull/1942)
|
||||
* More tests for the loading process:
|
||||
[\#1947](https://github.com/vector-im/vector-web/pull/1947)
|
||||
* Support for refactored login token handling
|
||||
[\#1946](https://github.com/vector-im/vector-web/pull/1946)
|
||||
* Various fixes and improvements to emojification.
|
||||
[\#1935](https://github.com/vector-im/vector-web/pull/1935)
|
||||
* More app-loading tests
|
||||
[\#1938](https://github.com/vector-im/vector-web/pull/1938)
|
||||
* Some tests of the application load process
|
||||
[\#1936](https://github.com/vector-im/vector-web/pull/1936)
|
||||
* Add 'enable labs' setting to sample config
|
||||
[\#1930](https://github.com/vector-im/vector-web/pull/1930)
|
||||
* Matthew/scalar
|
||||
[\#1928](https://github.com/vector-im/vector-web/pull/1928)
|
||||
* Fix unit tests
|
||||
[\#1929](https://github.com/vector-im/vector-web/pull/1929)
|
||||
* Wmwragg/mute mention state fix
|
||||
[\#1926](https://github.com/vector-im/vector-web/pull/1926)
|
||||
* CSS for deactivate account dialog
|
||||
[\#1919](https://github.com/vector-im/vector-web/pull/1919)
|
||||
* Wmwragg/mention state menu
|
||||
[\#1900](https://github.com/vector-im/vector-web/pull/1900)
|
||||
* Fix UnknownBody styling for #1901
|
||||
[\#1913](https://github.com/vector-im/vector-web/pull/1913)
|
||||
* Exclude olm from the webpack
|
||||
[\#1914](https://github.com/vector-im/vector-web/pull/1914)
|
||||
* Wmwragg/button updates
|
||||
[\#1912](https://github.com/vector-im/vector-web/pull/1912)
|
||||
* Wmwragg/button updates
|
||||
[\#1828](https://github.com/vector-im/vector-web/pull/1828)
|
||||
* CSS for device management UI
|
||||
[\#1909](https://github.com/vector-im/vector-web/pull/1909)
|
||||
* Fix a warning from RoomSubList
|
||||
[\#1908](https://github.com/vector-im/vector-web/pull/1908)
|
||||
* Fix notifications warning layout
|
||||
[\#1907](https://github.com/vector-im/vector-web/pull/1907)
|
||||
* Remove relayoutOnUpdate prop on gemini-scrollbar
|
||||
[\#1883](https://github.com/vector-im/vector-web/pull/1883)
|
||||
* Bump dependency versions
|
||||
[\#1842](https://github.com/vector-im/vector-web/pull/1842)
|
||||
* Wmwragg/mention state indicator round 2
|
||||
[\#1835](https://github.com/vector-im/vector-web/pull/1835)
|
||||
* Wmwragg/spinner fix
|
||||
[\#1822](https://github.com/vector-im/vector-web/pull/1822)
|
||||
* Wmwragg/mention state indicator
|
||||
[\#1823](https://github.com/vector-im/vector-web/pull/1823)
|
||||
* Revert "Presentation for inline link"
|
||||
[\#1809](https://github.com/vector-im/vector-web/pull/1809)
|
||||
* Wmwragg/modal restyle
|
||||
[\#1806](https://github.com/vector-im/vector-web/pull/1806)
|
||||
* Presentation for inline link
|
||||
[\#1799](https://github.com/vector-im/vector-web/pull/1799)
|
||||
* CSS for offline user colours
|
||||
[\#1798](https://github.com/vector-im/vector-web/pull/1798)
|
||||
* Wmwragg/typography updates
|
||||
[\#1776](https://github.com/vector-im/vector-web/pull/1776)
|
||||
* webpack: always use the olm from vector-web
|
||||
[\#1766](https://github.com/vector-im/vector-web/pull/1766)
|
||||
* feat: large emoji support
|
||||
[\#1718](https://github.com/vector-im/vector-web/pull/1718)
|
||||
* Autocomplete
|
||||
[\#1717](https://github.com/vector-im/vector-web/pull/1717)
|
||||
* #1664 Set a maximum height for codeblocks
|
||||
[\#1670](https://github.com/vector-im/vector-web/pull/1670)
|
||||
* CSS for device blocking
|
||||
[\#1688](https://github.com/vector-im/vector-web/pull/1688)
|
||||
* Fix joining rooms by typing the alias
|
||||
[\#1685](https://github.com/vector-im/vector-web/pull/1685)
|
||||
* Add ability to delete an alias from room directory
|
||||
[\#1680](https://github.com/vector-im/vector-web/pull/1680)
|
||||
* package.json: add olm as optionalDependency
|
||||
[\#1678](https://github.com/vector-im/vector-web/pull/1678)
|
||||
* Another go at enabling olm on vector.im/develop
|
||||
[\#1675](https://github.com/vector-im/vector-web/pull/1675)
|
||||
* CSS for unverify button
|
||||
[\#1661](https://github.com/vector-im/vector-web/pull/1661)
|
||||
* CSS fix for rooms with crypto enabled
|
||||
[\#1660](https://github.com/vector-im/vector-web/pull/1660)
|
||||
* Karma: fix warning by ignoring olm
|
||||
[\#1652](https://github.com/vector-im/vector-web/pull/1652)
|
||||
* Update for react-sdk dbkr/fix_peeking branch
|
||||
[\#1639](https://github.com/vector-im/vector-web/pull/1639)
|
||||
* Update README.md
|
||||
[\#1641](https://github.com/vector-im/vector-web/pull/1641)
|
||||
* Fix karma tests
|
||||
[\#1643](https://github.com/vector-im/vector-web/pull/1643)
|
||||
* Rich Text Editor
|
||||
[\#1553](https://github.com/vector-im/vector-web/pull/1553)
|
||||
* Fix RoomDirectory to join by alias whenever possible.
|
||||
[\#1615](https://github.com/vector-im/vector-web/pull/1615)
|
||||
* Make the config optional
|
||||
[\#1612](https://github.com/vector-im/vector-web/pull/1612)
|
||||
* CSS support for device verification
|
||||
[\#1610](https://github.com/vector-im/vector-web/pull/1610)
|
||||
* Don't use SdkConfig
|
||||
[\#1609](https://github.com/vector-im/vector-web/pull/1609)
|
||||
* serve config.json statically instead of bundling it
|
||||
[\#1516](https://github.com/vector-im/vector-web/pull/1516)
|
||||
|
||||
Changes in [0.7.3](https://github.com/vector-im/vector-web/releases/tag/v0.7.3) (2016-06-03)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.2...v0.7.3)
|
||||
|
||||
* Update to react-sdk 0.6.3
|
||||
|
||||
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
20
CHANGES.rst
Normal 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
|
||||
246
README.md
246
README.md
@@ -3,241 +3,43 @@ Vector/Web
|
||||
|
||||
Vector is a Matrix web client built using the Matrix React SDK (https://github.com/matrix-org/matrix-react-sdk).
|
||||
|
||||
Getting Started
|
||||
Getting started
|
||||
===============
|
||||
|
||||
The easiest way to test Vector is to just use the hosted copy at https://vector.im/beta.
|
||||
The develop branch is continuously deployed by Jenkins at https://vector.im/develop for
|
||||
those who like living dangerously.
|
||||
|
||||
To host your own copy of Vector, the quickest bet is to use a pre-built released version
|
||||
of Vector:
|
||||
|
||||
1. Download the latest version from https://vector.im/packages/
|
||||
1. Untar the tarball on your web server
|
||||
1. Move (or symlink) the vector-x.x.x directory to an appropriate name
|
||||
1. If desired, copy `config.sample.json` to `config.json` and edit it
|
||||
as desired. See below for details.
|
||||
1. Enter the URL into your browser and log into vector!
|
||||
|
||||
Important Security Note
|
||||
=======================
|
||||
|
||||
We do not recommend running Vector from the same domain name as your Matrix
|
||||
homeserver. The reason is the risk of XSS (cross-site-scripting) vulnerabilities
|
||||
that could occur if someone caused Vector to load and render malicious user generated
|
||||
content from a Matrix API which then had trusted access to Vector (or other apps) due
|
||||
to sharing the same domain.
|
||||
|
||||
We have put some coarse mitigations into place to try to protect against this situation,
|
||||
but it's still not good practice to do it in the first place.
|
||||
See https://github.com/vector-im/vector-web/issues/1977 for more details.
|
||||
|
||||
Building From Source
|
||||
====================
|
||||
|
||||
Vector is a modular webapp built with modern ES6 and requires a npm build system to build.
|
||||
|
||||
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. Configure the app by copying `config.sample.json` to `config.json` and modifying
|
||||
it (see below for details)
|
||||
1. `npm run package` to build a tarball to deploy. Untaring this file will give
|
||||
a version-specific directory containing all the files that need to go on your
|
||||
web server.
|
||||
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.
|
||||
|
||||
Note that `npm run package` is not supported on Windows, so Windows users can run `npm
|
||||
run build`, which will build all the necessary files into the `vector`
|
||||
directory. 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.
|
||||
|
||||
config.json
|
||||
===========
|
||||
|
||||
You can configure the app by copying `vector/config.sample.json` to
|
||||
`vector/config.json` and customising it:
|
||||
|
||||
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, adding an email address to your account,
|
||||
or inviting users via email address will not work. Matrix identity servers are
|
||||
very simple web services which map third party identifiers (currently only email
|
||||
addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html
|
||||
for more details. Currently the only public matrix identity servers are https://matrix.org
|
||||
and https://vector.im. In future identity servers will be decentralised.
|
||||
|
||||
|
||||
Running as a Desktop app
|
||||
========================
|
||||
|
||||
In future we'll do an official distribution of Vector as an desktop app. Meanwhile,
|
||||
there are a few options:
|
||||
|
||||
@asdf:matrix.org points out that you can use nativefier and it just works(tm):
|
||||
|
||||
```
|
||||
sudo npm install nativefier -g
|
||||
nativefier https://vector.im/beta/
|
||||
```
|
||||
|
||||
krisa has a dedicated electron project at https://github.com/krisak/vector-electron-desktop
|
||||
(although you should swap out the 'vector' folder for the latest vector tarball you want to run.
|
||||
Get a tarball from https://vector.im/packages or build your own - see Building From Source
|
||||
above).
|
||||
|
||||
There's also a (much) older electron distribution at https://github.com/stevenhammerton/vector-desktop
|
||||
With `npm start`, any changes you make to the source files will cause a rebuild so
|
||||
your changes will show up when you refresh.
|
||||
|
||||
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:
|
||||
|
||||
Before attempting to develop on Vector you **must** read the developer guide
|
||||
for `matrix-react-sdk` at https://github.com/matrix-org/matrix-react-sdk, which
|
||||
also defines the design, architecture and style for Vector too.
|
||||
|
||||
The idea of Vector is to be a relatively lightweight "skin" of customisations on
|
||||
top of the underlying `matrix-react-sdk`. `matrix-react-sdk` provides both the
|
||||
higher and lower level React components useful for building Matrix communication
|
||||
apps using React.
|
||||
|
||||
After creating a new component you must run `npm run reskindex` to regenerate
|
||||
the `component-index.js` for the app (used in future for skinning)
|
||||
|
||||
**However, as of July 2016 this layering abstraction is broken due to rapid
|
||||
development on Vector forcing `matrix-react-sdk` to move fast at the expense of
|
||||
maintaining a clear abstraction between the two.** Hacking on Vector inevitably
|
||||
means hacking equally on `matrix-react-sdk`, and there are bits of
|
||||
`matrix-react-sdk` behaviour incorrectly residing in the `vector-web` project
|
||||
(e.g. matrix-react-sdk specific CSS), and a bunch of Vector specific behaviour
|
||||
in the `matrix-react-sdk` (grep for Vector). This separation problem will be
|
||||
solved asap once development on Vector (and thus matrix-react-sdk) has
|
||||
stabilised. Until then, the two projects should basically be considered as a
|
||||
single unit. In particular, `matrix-react-sdk` issues are currently filed
|
||||
against `vector-web` in github.
|
||||
|
||||
Please note that Vector is intended to run correctly without access to the public
|
||||
internet. So please don't depend on resources (JS libs, CSS, images, fonts)
|
||||
hosted by external CDNs or servers but instead please package all dependencies
|
||||
into Vector itself.
|
||||
|
||||
Setting up a dev environment
|
||||
============================
|
||||
|
||||
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.
|
||||
|
||||
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. This development server also
|
||||
disables caching, so do NOT use it in production.
|
||||
1. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector.
|
||||
|
||||
When you make changes to `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 the 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`.
|
||||
|
||||
If any of these steps error with, `file table overflow`, you are probably on a mac
|
||||
which has a very low limit on max open files. Run `ulimit -Sn 1024` and try again.
|
||||
You'll need to do this in each new terminal you open before building vector.
|
||||
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.
|
||||
|
||||
Filing issues
|
||||
=============
|
||||
Deployment
|
||||
==========
|
||||
|
||||
All issues for Vector-web and Matrix-react-sdk should be filed at
|
||||
https://github.com/matrix-org/matrix-react-sdk/issues
|
||||
Just run `npm build` and then mount the `vector` directory on your webserver to
|
||||
actually serve up the app, which is entirely static content.
|
||||
|
||||
Triaging issues
|
||||
===============
|
||||
|
||||
Issues will be triaged by the core team using the following primary set of tags:
|
||||
|
||||
priority:
|
||||
P1: top priority; typically blocks releases.
|
||||
P2: one below that
|
||||
P3: non-urgent
|
||||
P4/P5: bluesky some day, who knows.
|
||||
|
||||
bug or feature:
|
||||
bug severity:
|
||||
* cosmetic - feature works functionally but UI/UX is broken.
|
||||
* critical - whole app doesn't work
|
||||
* major - entire feature doesn't work
|
||||
* minor - partially broken feature (but still usable)
|
||||
|
||||
* release blocker
|
||||
|
||||
* ui/ux (think of this as cosmetic)
|
||||
|
||||
* network (specific to network conditions)
|
||||
* platform (platform specific)
|
||||
|
||||
Enabling encryption
|
||||
===================
|
||||
|
||||
End-to-end encryption in Vector and Matrix is not yet considered ready for
|
||||
day-to-day use; it is experimental and should be considered only as a
|
||||
proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview
|
||||
of the current progress.
|
||||
|
||||
To enable the (very experimental) support, check the 'End-to-End Encryption'
|
||||
box in the 'Labs' section of the user settings (note that the labs are disabled
|
||||
on http://vector.im/beta: you will need to use http://vector.im/develop or your
|
||||
own deployment of vector). The Room Settings dialog will then show an
|
||||
'Encryption' setting; rooms for which you are an administrator will offer you
|
||||
the option of enabling encryption. Any messages sent in that room will then be
|
||||
encrypted.
|
||||
|
||||
Note that historical encrypted messages cannot currently be decoded - history
|
||||
is therefore lost when the page is reloaded.
|
||||
|
||||
4
config.json
Normal file
4
config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"default_hs_url": "https://matrix.org",
|
||||
"default_is_url": "https://vector.im"
|
||||
}
|
||||
@@ -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)
|
||||
36
jenkins.sh
36
jenkins.sh
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export NVM_DIR="/home/jenkins/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 6
|
||||
|
||||
set -x
|
||||
|
||||
npm install
|
||||
|
||||
# apparently npm 3.10.3 on node 6.4.0 doesn't upgrade #develop target with npm install unless explicitly asked.
|
||||
npm install matrix-react-sdk matrix-js-sdk
|
||||
|
||||
# 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)
|
||||
142
karma.conf.js
142
karma.conf.js
@@ -1,142 +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: [
|
||||
'node_modules/babel-polyfill/browser.js',
|
||||
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'),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// olm may not be installed, so avoid webpack warnings by
|
||||
// ignoring it.
|
||||
new webpack.IgnorePlugin(/^olm$/),
|
||||
],
|
||||
devtool: 'inline-source-map',
|
||||
},
|
||||
});
|
||||
};
|
||||
75
package.json
75
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vector-web",
|
||||
"version": "0.7.5-r3",
|
||||
"version": "0.0.1",
|
||||
"description": "Vector webapp",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
@@ -9,87 +9,40 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"style": "bundle.css",
|
||||
"matrix-react-parent": "matrix-react-sdk",
|
||||
"scripts": {
|
||||
"reskindex": "reskindex -h src/header",
|
||||
"build:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/",
|
||||
"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:staticfiles": "cpx -v node_modules/olm/olm.js vector/",
|
||||
"build": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle",
|
||||
"build:dev": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle:dev",
|
||||
"package": "scripts/package.sh",
|
||||
"start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/ -w",
|
||||
"build": "npm run build:css && npm run build:compile && npm run build:bundle",
|
||||
"start:js": "webpack -w src/vector/index.js vector/bundle.js",
|
||||
"start: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:staticfiles": "cpx -Lwv node_modules/olm/olm.js vector/",
|
||||
"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:staticfiles\" \"npm run start:emojione\" \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"start:prod": "parallelshell \"npm run start:staticfiles\" \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"clean": "rimraf lib vector/olm.js vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css* vector/emojione",
|
||||
"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"
|
||||
"start": "parallelshell \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map",
|
||||
"prepublish": "npm run build:css && npm run build:compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.5.0",
|
||||
"browser-request": "^0.3.3",
|
||||
"classnames": "^2.1.2",
|
||||
"draft-js": "^0.7.0",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"filesize": "^3.1.2",
|
||||
"flux": "~2.0.3",
|
||||
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "2.0.0-beta.4",
|
||||
"matrix-js-sdk": "0.5.6",
|
||||
"matrix-react-sdk": "0.6.5-r3",
|
||||
"modernizr": "^3.1.0",
|
||||
"linkifyjs": "^2.0.0-beta.4",
|
||||
"matrix-js-sdk": "^0.2.2",
|
||||
"matrix-react-sdk": "^0.0.1",
|
||||
"q": "^1.4.1",
|
||||
"react": "^15.2.1",
|
||||
"react-dnd": "^2.1.4",
|
||||
"react-dnd-html5-backend": "^2.1.2",
|
||||
"react-dom": "^15.2.1",
|
||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||
"sanitize-html": "^1.11.1",
|
||||
"ua-parser-js": "^0.7.10",
|
||||
"url": "^0.11.0"
|
||||
"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",
|
||||
"cpx": "^1.3.2",
|
||||
"css-raw-loader": "^0.1.1",
|
||||
"emojione": "^2.2.3",
|
||||
"expect": "^1.16.0",
|
||||
"fs-extra": "^0.30.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-perf": "^15.0",
|
||||
"react-addons-test-utils": "^15.0.1",
|
||||
"rimraf": "^2.4.3",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"webpack": "^1.12.14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-1.0.0.tgz"
|
||||
"uglifycss": "0.0.15"
|
||||
}
|
||||
}
|
||||
|
||||
12
release.sh
12
release.sh
@@ -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 "$@"
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
53
src/Avatar.js
Normal 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
71
src/ContextualMenu.js
Normal 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};
|
||||
},
|
||||
};
|
||||
@@ -1,57 +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.context_menus.MessageContextMenu'] = require('./components/views/context_menus/MessageContextMenu');
|
||||
module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu');
|
||||
module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu');
|
||||
module.exports.components['views.dialogs.ChangelogDialog'] = require('./components/views/dialogs/ChangelogDialog');
|
||||
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.rooms.BottomLeftMenuTile'] = require('./components/views/rooms/BottomLeftMenuTile');
|
||||
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.IntegrationsManager'] = require('./components/views/settings/IntegrationsManager');
|
||||
module.exports.components['views.settings.Notifications'] = require('./components/views/settings/Notifications');
|
||||
@@ -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> or
|
||||
<a href="https://getfirefox.com">Firefox</a> for the best experience.
|
||||
<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>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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);
|
||||
@@ -1,191 +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 Matrix = require("matrix-js-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() {
|
||||
if (this.props.userId) {
|
||||
var member = new Matrix.RoomMember(null, this.props.userId);
|
||||
return {
|
||||
phase: this.Phase.MemberInfo,
|
||||
member: member,
|
||||
}
|
||||
}
|
||||
else {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
else 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 }>
|
||||
{ membersBadge }
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25"/>
|
||||
{ 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.props.roomId && 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 member={this.state.member} key={this.props.roomId || this.props.userId} />
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,302 +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() {
|
||||
this.getPublicRooms();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
// dis.dispatch({
|
||||
// action: 'ui_opacity',
|
||||
// sideOpacity: 1.0,
|
||||
// middleOpacity: 1.0,
|
||||
// });
|
||||
},
|
||||
|
||||
getPublicRooms: 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,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* A limited interface for removing rooms from the directory.
|
||||
* Will set the room to not be publicly visible and delete the
|
||||
* default alias. In the long term, it would be better to allow
|
||||
* HS admins to do this through the RoomSettings interface, but
|
||||
* this needs SPEC-417.
|
||||
*/
|
||||
removeFromDirectory: function(room) {
|
||||
var alias = get_display_alias_for_room(room);
|
||||
var name = room.name || alias || "Unnamed room";
|
||||
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
var desc;
|
||||
if (alias) {
|
||||
desc = `Delete the room alias '${alias}' and remove '${name}' from the directory?`;
|
||||
} else {
|
||||
desc = `Remove '${name}' from the directory?`;
|
||||
}
|
||||
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Remove from Directory",
|
||||
description: desc,
|
||||
onFinished: (should_delete) => {
|
||||
if (!should_delete) return;
|
||||
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
var modal = Modal.createDialog(Loader);
|
||||
var step = `remove '${name}' from the directory.`;
|
||||
|
||||
MatrixClientPeg.get().setRoomDirectoryVisibility(room.room_id, 'private').then(() => {
|
||||
if (!alias) return;
|
||||
step = 'delete the alias.';
|
||||
return MatrixClientPeg.get().deleteAlias(alias);
|
||||
}).done(() => {
|
||||
modal.close();
|
||||
this.getPublicRooms();
|
||||
}, function(err) {
|
||||
modal.close();
|
||||
this.getPublicRooms();
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to "+step,
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onRoomClicked: function(room, ev) {
|
||||
if (ev.shiftKey) {
|
||||
ev.preventDefault();
|
||||
this.removeFromDirectory(room);
|
||||
} else {
|
||||
this.showRoom(room);
|
||||
}
|
||||
},
|
||||
|
||||
showRoomAlias: function(alias) {
|
||||
this.showRoom(null, alias);
|
||||
},
|
||||
|
||||
showRoom: function(room, room_alias) {
|
||||
var payload = {action: 'view_room'};
|
||||
if (room) {
|
||||
// Don't let the user view a room they won't be able to either
|
||||
// peek or join: fail earlier so they don't have to click back
|
||||
// to the directory.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!room_alias) {
|
||||
room_alias = get_display_alias_for_room(room);
|
||||
}
|
||||
|
||||
payload.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_alias || "Unnamed room",
|
||||
};
|
||||
}
|
||||
// It's not really possible to join Matrix rooms by ID because the HS has no way to know
|
||||
// which servers to start querying. However, there's no other way to join rooms in
|
||||
// this list without aliases at present, so if roomAlias isn't set here we have no
|
||||
// choice but to supply the ID.
|
||||
if (room_alias) {
|
||||
payload.room_alias = room_alias;
|
||||
} else {
|
||||
payload.room_id = room.room_id;
|
||||
}
|
||||
dis.dispatch(payload);
|
||||
},
|
||||
|
||||
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 name = rooms[i].name || get_display_alias_for_room(rooms[i]) || "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.onRoomClicked.bind(self, rooms[i])}
|
||||
// cancel onMouseDown otherwise shift-clicking highlights text
|
||||
onMouseDown={(ev) => {ev.preventDefault();}}
|
||||
>
|
||||
<td className="mx_RoomDirectory_roomAvatar">
|
||||
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
||||
name={ name } idName={ name }
|
||||
url={ ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
rooms[i].avatar_url, 24, 24, "crop") } />
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomDescription">
|
||||
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||
{ perms }
|
||||
<div className="mx_RoomDirectory_topic"
|
||||
onClick={ function(e) { e.stopPropagation() } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }}/>
|
||||
<div className="mx_RoomDirectory_alias">{ get_display_alias_for_room(rooms[i]) }</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.showRoomAlias(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">
|
||||
<table ref="directory_table" className="mx_RoomDirectory_table">
|
||||
<tbody>
|
||||
{ this.getRows(this.state.roomAlias) }
|
||||
</tbody>
|
||||
</table>
|
||||
</GeminiScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
||||
// but works with the objects we get from the public room list
|
||||
function get_display_alias_for_room(room) {
|
||||
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
||||
}
|
||||
@@ -1,429 +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,
|
||||
|
||||
// undefined if no room is selected (eg we are showing settings)
|
||||
selectedRoom: React.PropTypes.string,
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
lexicographicalComparator: function(roomA, roomB) {
|
||||
return roomA.name > roomB.name ? 1 : -1;
|
||||
},
|
||||
|
||||
// Generates the manual comparator using the given list
|
||||
manualComparator: function(roomA, roomB) {
|
||||
if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
|
||||
|
||||
// Make sure the room tag has an order element, if not set it to be the bottom
|
||||
var a = roomA.tags[this.props.tagName].order;
|
||||
var b = roomB.tags[this.props.tagName].order;
|
||||
|
||||
// Order undefined room tag orders to the bottom
|
||||
if (a === undefined && b !== undefined) {
|
||||
return 1;
|
||||
} else if (a !== undefined && b === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a == b ? this.lexicographicalComparator(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;
|
||||
|
||||
// Fix undefined orders here, and make sure the backend gets updated as well
|
||||
this._fixUndefinedOrder(list);
|
||||
|
||||
//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();
|
||||
},
|
||||
|
||||
// Fix any undefined order elements of a room in a manual ordered list
|
||||
// room.tag[tagname].order
|
||||
_fixUndefinedOrder: function(list) {
|
||||
if (this.props.order === "manual") {
|
||||
var order = 0.0;
|
||||
var self = this;
|
||||
|
||||
// Find the highest (lowest position) order of a room in a manual ordered list
|
||||
list.forEach(function(room) {
|
||||
if (room.tags.hasOwnProperty(self.props.tagName)) {
|
||||
if (order < room.tags[self.props.tagName].order) {
|
||||
order = room.tags[self.props.tagName].order;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fix any undefined order elements of a room in a manual ordered list
|
||||
// Do this one at a time, as each time a rooms tag data is updated the RoomList
|
||||
// gets triggered and another list is passed in. Doing it one at a time means that
|
||||
// we always correctly calculate the highest order for the list - stops multiple
|
||||
// rooms getting the same order. This is only really relevant for the first time this
|
||||
// is run with historical room tag data, after that there should only be undefined
|
||||
// in the list at a time anyway.
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
|
||||
MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() {
|
||||
// Do any final stuff here
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + self.props.tagName + " to room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
@@ -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="<"/>
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
toggleCollapse =
|
||||
<div className="mx_SearchBox_minimise" onClick={ this.onToggleCollapse.bind(this, false) }>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16" alt="<"/>
|
||||
</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/icons-search-copy.svg" width="13" height="13"
|
||||
/>,
|
||||
<input
|
||||
key="searchfield"
|
||||
type="text"
|
||||
ref="search"
|
||||
className="mx_SearchBox_search"
|
||||
value={ this.state.searchTerm }
|
||||
onChange={ this.onChange }
|
||||
placeholder="Filter room names"
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return (
|
||||
<div className="mx_SearchBox">
|
||||
{ searchControls }
|
||||
{ toggleCollapse }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,155 +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
|
||||
}, 'mx_Dialog_viewsource');
|
||||
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_MessageContextMenu_field" onClick={this.onResendClick}>
|
||||
Resend
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!eventStatus) { // sent
|
||||
redactButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
||||
Redact
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventStatus === "queued" || eventStatus === "not_sent") {
|
||||
cancelButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
||||
Cancel Sending
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
viewSourceButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
|
||||
View Source
|
||||
</div>
|
||||
);
|
||||
|
||||
if (this.props.eventTileOps) {
|
||||
if (this.props.eventTileOps.isWidgetHidden()) {
|
||||
unhidePreviewButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
|
||||
Unhide Preview
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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_MessageContextMenu_field">
|
||||
<a href={ "https://matrix.to/#/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() }
|
||||
target="_blank" onClick={ this.onPermalinkClick }>Permalink</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{resendButton}
|
||||
{redactButton}
|
||||
{cancelButton}
|
||||
{viewSourceButton}
|
||||
{unhidePreviewButton}
|
||||
{permalinkButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,144 +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 q = require("q");
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'NotificationStateContextMenu',
|
||||
|
||||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
/* callback called when the menu is dismissed */
|
||||
onFinished: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
roomNotifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._unmounted = false;
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._unmounted = true;
|
||||
},
|
||||
|
||||
_save: function(newState) {
|
||||
const oldState = this.state.roomNotifState;
|
||||
const roomId = this.props.room.roomId;
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
if (cli.isGuest()) return;
|
||||
|
||||
this.setState({
|
||||
roomNotifState: newState,
|
||||
});
|
||||
RoomNotifs.setRoomNotifsState(this.props.room.roomId, newState).done(() => {
|
||||
// delay slightly so that the user can see their state change
|
||||
// before closing the menu
|
||||
return q.delay(500).then(() => {
|
||||
if (this._unmounted) return;
|
||||
// Close the context menu
|
||||
if (this.props.onFinished) {
|
||||
this.props.onFinished();
|
||||
};
|
||||
});
|
||||
}, (error) => {
|
||||
// TODO: some form of error notification to the user
|
||||
// to inform them that their state change failed.
|
||||
// For now we at least set the state back
|
||||
if (this._unmounted) return;
|
||||
this.setState({
|
||||
roomNotifState: oldState,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_onClickAlertMe: function() {
|
||||
this._save(RoomNotifs.ALL_MESSAGES_LOUD);
|
||||
},
|
||||
|
||||
_onClickAllNotifs: function() {
|
||||
this._save(RoomNotifs.ALL_MESSAGES);
|
||||
},
|
||||
|
||||
_onClickMentions: function() {
|
||||
this._save(RoomNotifs.MENTIONS_ONLY);
|
||||
},
|
||||
|
||||
_onClickMute: function() {
|
||||
this._save(RoomNotifs.MUTE);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var alertMeClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
|
||||
});
|
||||
|
||||
var allNotifsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES,
|
||||
});
|
||||
|
||||
var mentionsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': this.state.roomNotifState == RoomNotifs.MENTIONS_ONLY,
|
||||
});
|
||||
|
||||
var muteNotifsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': this.state.roomNotifState == RoomNotifs.MUTE,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_NotificationStateContextMenu_picker" >
|
||||
<img src="img/notif-slider.svg" width="20" height="107" />
|
||||
</div>
|
||||
<div className={ alertMeClasses } onClick={this._onClickAlertMe} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
|
||||
All messages (loud)
|
||||
</div>
|
||||
<div className={ allNotifsClasses } onClick={this._onClickAllNotifs} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off.svg" width="16" height="12" />
|
||||
All messages
|
||||
</div>
|
||||
<div className={ mentionsClasses } onClick={this._onClickMentions} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
|
||||
Mentions only
|
||||
</div>
|
||||
<div className={ muteNotifsClasses } onClick={this._onClickMute} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute.svg" width="16" height="12" />
|
||||
Mute
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,201 +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 q = require("q");
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTagContextMenu',
|
||||
|
||||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
/* callback called when the menu is dismissed */
|
||||
onFinished: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
|
||||
isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
|
||||
};
|
||||
},
|
||||
|
||||
_toggleTag: function(tagNameOn, tagNameOff) {
|
||||
var self = this;
|
||||
const roomId = this.props.room.roomId;
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (!cli.isGuest()) {
|
||||
q.delay(500).then(function() {
|
||||
if (tagNameOff !== null && tagNameOff !== undefined) {
|
||||
cli.deleteRoomTag(roomId, tagNameOff).finally(function() {
|
||||
// Close the context menu
|
||||
if (self.props.onFinished) {
|
||||
self.props.onFinished();
|
||||
};
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to remove tag " + tagNameOff + " from room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (tagNameOn !== null && tagNameOn !== undefined) {
|
||||
// If the tag ordering meta data is required, it is added by
|
||||
// the RoomSubList when it sorts its rooms
|
||||
cli.setRoomTag(roomId, tagNameOn, {}).finally(function() {
|
||||
// Close the context menu
|
||||
if (self.props.onFinished) {
|
||||
self.props.onFinished();
|
||||
};
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + tagNameOn + " to room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onClickFavourite: function() {
|
||||
// Tag room as 'Favourite'
|
||||
if (!this.state.isFavourite && this.state.isLowPriority) {
|
||||
this.setState({
|
||||
isFavourite: true,
|
||||
isLowPriority: false,
|
||||
});
|
||||
this._toggleTag("m.favourite", "m.lowpriority");
|
||||
} else if (this.state.isFavourite) {
|
||||
this.setState({isFavourite: false});
|
||||
this._toggleTag(null, "m.favourite");
|
||||
} else if (!this.state.isFavourite) {
|
||||
this.setState({isFavourite: true});
|
||||
this._toggleTag("m.favourite");
|
||||
}
|
||||
},
|
||||
|
||||
_onClickLowPriority: function() {
|
||||
// Tag room as 'Low Priority'
|
||||
if (!this.state.isLowPriority && this.state.isFavourite) {
|
||||
this.setState({
|
||||
isFavourite: false,
|
||||
isLowPriority: true,
|
||||
});
|
||||
this._toggleTag("m.lowpriority", "m.favourite");
|
||||
} else if (this.state.isLowPriority) {
|
||||
this.setState({isLowPriority: false});
|
||||
this._toggleTag(null, "m.lowpriority");
|
||||
} else if (!this.state.isLowPriority) {
|
||||
this.setState({isLowPriority: true});
|
||||
this._toggleTag("m.lowpriority");
|
||||
}
|
||||
},
|
||||
|
||||
_onClickLeave: function() {
|
||||
// Leave room
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
|
||||
// Close the context menu
|
||||
if (this.props.onFinished) {
|
||||
this.props.onFinished();
|
||||
};
|
||||
},
|
||||
|
||||
_onClickForget: function() {
|
||||
// FIXME: duplicated with RoomSettings (and dead code in RoomView)
|
||||
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
}, function(err) {
|
||||
var errCode = err.errcode || "unknown error code";
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: `Failed to forget room (${errCode})`
|
||||
});
|
||||
});
|
||||
|
||||
// Close the context menu
|
||||
if (this.props.onFinished) {
|
||||
this.props.onFinished();
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var myMember = this.props.room.getMember(myUserId);
|
||||
|
||||
var favouriteClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isFavourite,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var lowPriorityClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isLowPriority,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var leaveClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': false,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
if (myMember && myMember.membership === "leave") {
|
||||
return (
|
||||
<div>
|
||||
<div className={ leaveClasses } onClick={ this._onClickForget } >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_delete.svg" width="15" height="15" />
|
||||
Forget
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ favouriteClasses } onClick={this._onClickFavourite} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_fave.svg" width="15" height="15" />
|
||||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_fave_on.svg" width="15" height="15" />
|
||||
Favourite
|
||||
</div>
|
||||
<div className={ lowPriorityClasses } onClick={this._onClickLowPriority} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_low.svg" width="15" height="15" />
|
||||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_low_on.svg" width="15" height="15" />
|
||||
Low Priority
|
||||
</div>
|
||||
<hr className="mx_RoomTagContextMenu_separator" />
|
||||
<div className={ leaveClasses } onClick={(myMember && myMember.membership === "join") ? this._onClickLeave : null} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_delete.svg" width="15" height="15" />
|
||||
Leave
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import request from 'browser-request';
|
||||
|
||||
const REPOS = ['vector-im/vector-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
|
||||
|
||||
export default class ChangelogDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const version = this.props.newVersion.split('-');
|
||||
const version2 = this.props.version.split('-');
|
||||
if(version == null || version2 == null) return;
|
||||
for(let i=0; i<REPOS.length; i++) {
|
||||
const oldVersion = version2[2*i+1];
|
||||
const newVersion = version[2*i+1];
|
||||
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
|
||||
if(body == null) return;
|
||||
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
||||
|
||||
const logs = REPOS.map(repo => {
|
||||
if (this.state[repo] == null) return <Spinner key={repo} />;
|
||||
return (
|
||||
<div key={repo}>
|
||||
<h2>{repo}</h2>
|
||||
{this.state[repo].map(commit =>
|
||||
<div key={commit.commit.url}><a href={commit.commit.url}>{commit.commit.message}</a></div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
const content = (
|
||||
<div className="mx_ChangelogDialog_content">
|
||||
{this.props.version == null || this.props.newVersion == null ? <h2>Unavailable</h2> : logs}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title="Changelog"
|
||||
description={content}
|
||||
button="Update"
|
||||
onFinished={this.props.onFinished}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ChangelogDialog.propTypes = {
|
||||
version: React.PropTypes.string.isRequired,
|
||||
newVersion: React.PropTypes.string.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -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" rel="noopener">{ 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" rel="noopener">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,52 +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');
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
|
||||
export default function NewVersionBar(props) {
|
||||
const onChangelogClicked = () => {
|
||||
const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog');
|
||||
|
||||
Modal.createDialog(ChangelogDialog, {
|
||||
version: props.version,
|
||||
newVersion: props.newVersion,
|
||||
onFinished: (update) => {
|
||||
if(update) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
A new version of Vector is available. Refresh your browser.
|
||||
</div>
|
||||
<button className="mx_MatrixToolbar_action" onClick={onChangelogClicked}>Changelog</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NewVersionBar.propTypes = {
|
||||
version: React.PropTypes.string.isRequired,
|
||||
newVersion: React.PropTypes.string.isRequired,
|
||||
};
|
||||
@@ -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';
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,56 +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 MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'IntegrationsManager',
|
||||
|
||||
propTypes: {
|
||||
src: React.PropTypes.string.isRequired, // the source of the integration manager being embedded
|
||||
onFinished: React.PropTypes.func.isRequired, // callback when the lightbox is dismissed
|
||||
},
|
||||
|
||||
// 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();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_IntegrationsManager">
|
||||
<iframe src={ this.props.src }></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,866 +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 notifications = require('../../../notifications');
|
||||
|
||||
// TODO: this "view" component still has far too 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,
|
||||
// The brand string set when creating an email pusher
|
||||
brand: React.PropTypes.string,
|
||||
},
|
||||
|
||||
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 = {}
|
||||
data['brand'] = this.props.brand || 'Vector';
|
||||
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 can’t 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/>& sound</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{ this.renderNotifRulesTableRows() }
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{ advancedSettings }
|
||||
|
||||
{ devicesSection }
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 = {
|
||||
};
|
||||
96
src/controllers/molecules/voip/CallView.js
Normal file
96
src/controllers/molecules/voip/CallView.js
Normal 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";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
182
src/controllers/organisms/RoomList.js
Normal file
182
src/controllers/organisms/RoomList.js
Normal 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}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
506
src/controllers/organisms/RoomView.js
Normal file
506
src/controllers/organisms/RoomView.js
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
58
src/controllers/templates/Register.js
Normal file
58
src/controllers/templates/Register.js
Normal 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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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: "override",
|
||||
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,
|
||||
}
|
||||
}),
|
||||
};
|
||||
22
src/skins/vector/css/atoms/MemberAvatar.css
Normal file
22
src/skins/vector/css/atoms/MemberAvatar.css
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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,18 +22,11 @@ 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;
|
||||
/* This should render the fonts the same accross browsers */
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
|
||||
div.error {
|
||||
@@ -41,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;
|
||||
}
|
||||
@@ -51,67 +44,55 @@ h2 {
|
||||
a:hover,
|
||||
a:link,
|
||||
a:visited {
|
||||
color: #76cfa6;
|
||||
color: #80CEF4;
|
||||
}
|
||||
|
||||
input[type=text].error, input[type=password].error {
|
||||
border: 1px solid red;
|
||||
.mx_ContextualMenu_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1.0;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
input[type=text]:focus, textarea:focus {
|
||||
border: 1px solid #76CFA6;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
.mx_ContextualMenu {
|
||||
border: 1px solid #a9dbf4;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
color: #747474;
|
||||
position: fixed;
|
||||
z-index: 2001;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
/* Prevent ugly dotted highlight around selected elements in Firefox */
|
||||
::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
.mx_ContextualMenu_chevron {
|
||||
padding: 12px;
|
||||
position: absolute;
|
||||
right: -21px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
/* Expand thumbs on hoverover */
|
||||
.gm-scrollbar {
|
||||
border-radius: 5px ! important;
|
||||
.mx_ContextualMenu_field {
|
||||
padding: 3px 6px 3px 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.gm-scrollbar.-vertical {
|
||||
width: 6px;
|
||||
transition: width 120ms ease-out ! important;
|
||||
}
|
||||
.gm-scrollbar.-vertical:hover,
|
||||
.gm-scrollbar.-vertical:active {
|
||||
width: 8px;
|
||||
transition: width 120ms ease-out ! important;
|
||||
}
|
||||
.gm-scrollbar.-horizontal {
|
||||
height: 6px;
|
||||
transition: height 120ms ease-out ! important;
|
||||
}
|
||||
.gm-scrollbar.-horizontal:hover,
|
||||
.gm-scrollbar.-horizontal:active {
|
||||
height: 8px;
|
||||
transition: height 120ms ease-out ! important;
|
||||
|
||||
|
||||
.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%;
|
||||
@@ -128,152 +109,52 @@ input[type=text]:focus, textarea:focus {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Spinner Dialog overide */
|
||||
.mx_Dialog_wrapper.mx_Dialog_spinner .mx_Dialog {
|
||||
width: auto;
|
||||
border-radius: 8px;
|
||||
padding-left: 0px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* View Source Dialog overide */
|
||||
.mx_Dialog_wrapper.mx_Dialog_viewsource .mx_Dialog {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.mx_Dialog {
|
||||
background-color: #fff;
|
||||
color: #747474;
|
||||
z-index: 4010;
|
||||
text-align: center;
|
||||
z-index: 2010;
|
||||
font-weight: 300;
|
||||
font-size: 15px;
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
padding-left: 58px;
|
||||
width: 60%;
|
||||
max-width: 704px;
|
||||
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.mx_Dialog_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #e9e9e9;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.mx_Dialog_lightbox .mx_Dialog_background {
|
||||
opacity: 0.85;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.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 {
|
||||
margin: 24px 58px 68px 0;
|
||||
font-size: 14px;
|
||||
color: #4a4a4a;
|
||||
word-wrap: break-word;
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.mx_Dialog_buttons {
|
||||
padding-bottom: 36px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.mx_Dialog button, .mx_Dialog input[type="submit"] {
|
||||
.mx_Dialog button {
|
||||
border: 0px;
|
||||
height: 36px;
|
||||
border-radius: 40px;
|
||||
border: solid 1px #76cfa6;
|
||||
border-radius: 36px;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
margin-left: 0px;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: #80cef4;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
padding-left: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
outline: none;
|
||||
|
||||
color: #76cfa6;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary {
|
||||
color: #fff;
|
||||
background-color: #76cfa6;
|
||||
}
|
||||
|
||||
.mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger {
|
||||
background-color: #ff0064;
|
||||
border: solid 1px #ff0064;
|
||||
}
|
||||
|
||||
.mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled {
|
||||
background-color: #777777;
|
||||
border: solid 1px #777777;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mx_Dialog_title {
|
||||
min-height: 16px;
|
||||
padding-top: 40px;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 1.4;
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
.mx_Dialog_title.danger {
|
||||
color: #ff0064;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.mx_emojione {
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background-color: #76CFA6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: #76CFA6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/** green button with rounded corners */
|
||||
.mx_textButton {
|
||||
color: #fff;
|
||||
background-color: #76cfa6;
|
||||
border-radius: 17px;
|
||||
text-align: center;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.mx_ErrorDialogTitle,
|
||||
.mx_QuestionDialogTitle {
|
||||
min-height: 16px;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #a9dbf4;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,106 +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_ContextualMenu_wrapper {
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1.0;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu {
|
||||
border: solid 1px rgba(187, 187, 187, 0.5);
|
||||
border-radius: 4px;
|
||||
background-color: #f6f6f6;
|
||||
color: #4a4a4a;
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
z-index: 2001;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu.mx_ContextualMenu_right {
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_chevron_right {
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 0px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-left: 8px solid rgba(187, 187, 187, 0.5);
|
||||
border-bottom: 8px solid transparent;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_chevron_right:after {
|
||||
content:'';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 7px solid transparent;
|
||||
border-left: 7px solid #f6f6f6;
|
||||
border-bottom: 7px solid transparent;
|
||||
position:absolute;
|
||||
top: -7px;
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu.mx_ContextualMenu_left {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_chevron_left {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 0px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-right: 8px solid rgba(187, 187, 187, 0.5);
|
||||
border-bottom: 8px solid transparent;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_chevron_left:after{
|
||||
content:'';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 7px solid transparent;
|
||||
border-right: 7px solid #f6f6f6;
|
||||
border-bottom: 7px solid transparent;
|
||||
position:absolute;
|
||||
top: -7px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_field {
|
||||
padding: 3px 6px 3px 6px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_spinner {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,70 +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: 16px;
|
||||
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;
|
||||
margin-top: 5px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_SearchBox_closeButton {
|
||||
cursor: pointer;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,202 +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_button.danger {
|
||||
background-color: #ff0064;
|
||||
}
|
||||
|
||||
.mx_UserSettings_section {
|
||||
margin-left: 63px;
|
||||
margin-top: 28px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.mx_UserSettings_toggle input {
|
||||
width: 16px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mx_UserSettings_toggle label {
|
||||
padding-bottom: 21px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -1,36 +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_MultiInviteDialog ul {
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mx_MultiInviteDialog li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.mx_MultiInviteDialog_statusText {
|
||||
text-indent: 20px;
|
||||
}
|
||||
|
||||
.mx_MultiInviteDialog p.error {
|
||||
color: #ff0064;
|
||||
}
|
||||
|
||||
.mx_MultiInviteDialog p.invited {
|
||||
color: #76cfa6;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
.mx_UserPill {
|
||||
color: white;
|
||||
background-color: #76cfa6;
|
||||
padding: 2px 8px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.mx_RoomPill {
|
||||
background-color: white;
|
||||
color: #76cfa6;
|
||||
border: 1px solid #76cfa6;
|
||||
padding: 2px 8px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.mx_Markdown_BOLD {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mx_Markdown_ITALIC {
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -1,47 +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_MImageBody {
|
||||
display: block;
|
||||
margin-right: 34px;
|
||||
}
|
||||
|
||||
.mx_MImageBody_thumbnail {
|
||||
max-width: 100%;
|
||||
/*
|
||||
background-color: #fff;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 1px;
|
||||
*/
|
||||
}
|
||||
|
||||
.mx_MImageBody_download {
|
||||
color: #76cfa6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MImageBody_download a {
|
||||
color: #76cfa6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mx_MImageBody_download object {
|
||||
margin-left: -16px;
|
||||
padding-right: 4px;
|
||||
margin-top: -4px;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
.mx_Autocomplete {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
border: 1px solid #e5e5e5;
|
||||
background: white;
|
||||
border-bottom: none;
|
||||
border-radius: 4px 4px 0 0;
|
||||
max-height: 50vh;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.mx_Autocomplete_ProviderSection {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_container_pill {
|
||||
margin: 12px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* a "block" completion takes up a whole line */
|
||||
.mx_Autocomplete_Completion_block {
|
||||
height: 34px;
|
||||
display: flex;
|
||||
padding: 0 12px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_block * {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_pill {
|
||||
border-radius: 17px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_pill * {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
/* container for pill-style completions */
|
||||
.mx_Autocomplete_Completion_container_pill {
|
||||
margin: 12px;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion.selected {
|
||||
background: #f6f6f6;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_provider_name {
|
||||
margin: 12px;
|
||||
color: #454545;
|
||||
font-weight: 400;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* styling for common completion elements */
|
||||
.mx_Autocomplete_Completion_subtitle {
|
||||
font-style: italic;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mx_Autocomplete_Completion_description {
|
||||
color: gray;
|
||||
}
|
||||
@@ -1,112 +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,
|
||||
.mx_EntityTile_offline_beenactive .mx_EntityTile_avatar,
|
||||
.mx_EntityTile_offline_beenactive .mx_EntityTile_name,
|
||||
.mx_EntityTile_offline_beenactive .mx_EntityTile_name_hover
|
||||
{
|
||||
opacity: 0.66;
|
||||
}
|
||||
|
||||
.mx_EntityTile_offline_neveractive .mx_EntityTile_avatar,
|
||||
.mx_EntityTile_offline_neveractive .mx_EntityTile_name,
|
||||
.mx_EntityTile_offline_neveractive .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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,267 +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;
|
||||
padding-top: 18px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_EventTile.mx_EventTile_info {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.mx_EventTile_avatar {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 8px;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.mx_EventTile.mx_EventTile_info .mx_EventTile_avatar {
|
||||
top: 8px;
|
||||
left: 44px;
|
||||
}
|
||||
|
||||
.mx_EventTile_continuation {
|
||||
padding-top: 0px ! important;
|
||||
}
|
||||
|
||||
.mx_EventTile_verified {
|
||||
background-color: #eaf5f0;
|
||||
}
|
||||
|
||||
.mx_EventTile_unverified {
|
||||
background-color: #ffa0a0;
|
||||
}
|
||||
|
||||
.mx_EventTile .mx_SenderProfile {
|
||||
color: #454545;
|
||||
opacity: 0.5;
|
||||
font-size: 14px;
|
||||
display: block; /* anti-zalgo, with overflow hidden */
|
||||
overflow-y: hidden;
|
||||
cursor: pointer;
|
||||
padding-left: 65px; /* left gutter */
|
||||
padding-bottom: 0px;
|
||||
padding-top: 0px;
|
||||
margin: 0px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.mx_EventTile .mx_MessageTimestamp {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
white-space: nowrap;
|
||||
color: #acacac;
|
||||
font-size: 11px;
|
||||
left: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mx_EventTile_line {
|
||||
position: relative;
|
||||
/* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */
|
||||
margin-right: 110px;
|
||||
padding-left: 65px; /* left gutter */
|
||||
padding-top: 4px;
|
||||
padding-bottom: 2px;
|
||||
border-radius: 4px;
|
||||
min-height: 24px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
/* HACK to override line-height which is already marked important elsewhere */
|
||||
.mx_EventTile_bigEmoji.mx_EventTile_bigEmoji {
|
||||
font-size: 48px ! important;
|
||||
line-height: 48px ! important;
|
||||
}
|
||||
|
||||
/* 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 .mx_EventTile_line {
|
||||
border-left: #76cfa6 5px solid;
|
||||
padding-left: 60px;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.mx_EventTile:hover .mx_EventTile_line,
|
||||
.mx_EventTile.menu .mx_EventTile_line
|
||||
{
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.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_last .mx_MessageTimestamp,
|
||||
.mx_EventTile:hover .mx_MessageTimestamp,
|
||||
.mx_EventTile.menu .mx_MessageTimestamp
|
||||
{
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mx_EventTile_selected .mx_MessageTimestamp {
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
.mx_EventTile_editButton {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
.mx_EventTile:hover .mx_EventTile_editButton,
|
||||
.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;
|
||||
top: 29px;
|
||||
}
|
||||
|
||||
.mx_EventTile_continuation .mx_EventTile_readAvatars,
|
||||
.mx_EventTile_info .mx_EventTile_readAvatars
|
||||
{
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.mx_EventTile_readAvatars .mx_BaseAvatar {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mx_EventTile_readAvatarRemainder {
|
||||
color: #acacac;
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
margin-right: 34px;
|
||||
}
|
||||
|
||||
/* De-zalgoing */
|
||||
.mx_EventTile_body {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* Various markdown overrides */
|
||||
|
||||
.mx_EventTile_content .markdown-body {
|
||||
font-family: inherit ! important;
|
||||
white-space: normal ! important;
|
||||
line-height: inherit ! important;
|
||||
color: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,57 +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_MemberDeviceInfo {
|
||||
font-size: 12px;
|
||||
display: table-row;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo div {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo_textButton {
|
||||
color: #fff;
|
||||
background-color: #76cfa6;
|
||||
border-radius: 17px;
|
||||
text-align: center;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_verified,
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_unverified,
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_blocked {
|
||||
color: #fff;
|
||||
width: 17px;
|
||||
border-radius: 17px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_verified {
|
||||
background-color: #76cfa6;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_unverified {
|
||||
background-color: #eca46f;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo div.mx_MemberDeviceInfo_blocked {
|
||||
background-color: #e55e5e;
|
||||
}
|
||||
@@ -1,80 +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%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_devices {
|
||||
display: table;
|
||||
border-spacing: 5px;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,124 +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_MessageComposer_wrapper {
|
||||
max-width: 960px;
|
||||
vertical-align: middle;
|
||||
margin: auto;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_autocomplete_wrapper {
|
||||
position: relative;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_MessageComposer .mx_MessageComposer_avatar {
|
||||
padding-left: 10px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer .mx_MessageComposer_avatar .mx_BaseAvatar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_composecontrols {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_noperm_error {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_input {
|
||||
flex: 1;
|
||||
vertical-align: middle;
|
||||
min-height: 60px;
|
||||
max-height: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
font-size: 14px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.mx_MessageComposer_input_rte {
|
||||
border-top: 2px solid #76cfa6; /* placeholder RTE indicator */
|
||||
}
|
||||
|
||||
.mx_MessageComposer_input .DraftEditor-root {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_input textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
border: 0px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
color: #454545;
|
||||
background-color: #fff;
|
||||
font-size: 14px;
|
||||
|
||||
/* needed for FF */
|
||||
font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
|
||||
}
|
||||
|
||||
/* hack for FF as vertical alignment of custom placeholder text is broken */
|
||||
.mx_MessageComposer_input textarea::-moz-placeholder {
|
||||
line-height: 100%;
|
||||
color: #76cfa6;
|
||||
opacity: 1.0;
|
||||
}
|
||||
.mx_MessageComposer_input textarea::-webkit-input-placeholder {
|
||||
color: #76cfa6;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_upload,
|
||||
.mx_MessageComposer_hangup,
|
||||
.mx_MessageComposer_voicecall,
|
||||
.mx_MessageComposer_videocall {
|
||||
/*display: table-cell;*/
|
||||
/*vertical-align: middle;*/
|
||||
/*padding-left: 10px;*/
|
||||
padding-right: 5px;
|
||||
cursor: pointer;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_upload object,
|
||||
.mx_MessageComposer_hangup object,
|
||||
.mx_MessageComposer_voicecall object,
|
||||
.mx_MessageComposer_videocall object {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -1,250 +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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-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 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_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 {
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_button object {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_voipButton {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_voipButtons {
|
||||
margin-top: 18px;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,202 +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_leaveButton {
|
||||
height: 36px;
|
||||
background-color: #76cfa6;
|
||||
border-radius: 36px;
|
||||
margin-right: 8px;
|
||||
color: #fff;
|
||||
line-height: 34px;
|
||||
text-align: center;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -1,191 +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 {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.mx_RoomTile_nameContainer {
|
||||
display: inline-block;
|
||||
width: 180px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.mx_RoomTile_avatar {
|
||||
display: inline-block;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 16px;
|
||||
padding-right: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_RoomTile_avatar_container:hover:before,
|
||||
.mx_RoomTile_avatar_container.mx_RoomTile_avatar_roomTagMenu:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-radius: 40px;
|
||||
background-image: url("img/icons_ellipsis.svg");
|
||||
background-size: 25px;
|
||||
left: 15px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.mx_RoomTile_avatar_container:hover:after,
|
||||
.mx_RoomTile_avatar_container.mx_RoomTile_avatar_roomTagMenu:after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-radius: 40px;
|
||||
background: #4A4A4A;
|
||||
top: 5px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
opacity: 0.6;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_avatar_container:hover:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_avatar_container:hover:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_RoomTile_name {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 165px;
|
||||
vertical-align: middle;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 3px;
|
||||
color: rgba(69, 69, 69, 0.8);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_RoomTile_ellipsis .mx_RoomTile_name {
|
||||
font-style: italic;
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
.mx_RoomTile_invite {
|
||||
/* color: rgba(69, 69, 69, 0.5);
|
||||
*/
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_nameContainer {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_badge {
|
||||
top: -2px;
|
||||
min-width: 12px;
|
||||
height: 16px;
|
||||
border-radius: 16px;
|
||||
padding: 0px 4px 0px 4px;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
/* Hide the bottom of speech bubble */
|
||||
.collapsed .mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* This is the bottom of the speech bubble */
|
||||
.mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 6px;
|
||||
border-top: 8px solid #ff0064;
|
||||
border-right: 10px solid transparent;
|
||||
}
|
||||
|
||||
.mx_RoomTile_badge {
|
||||
display: inline-block;
|
||||
min-width: 19px;
|
||||
height: 17px;
|
||||
position: absolute;
|
||||
right: 8px; /*gutter */
|
||||
top: 9px;
|
||||
border-radius: 14px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.mx_RoomTile .mx_RoomTile_badge.mx_RoomTile_badgeButton,
|
||||
.mx_RoomTile.mx_RoomTile_notificationStateMenu .mx_RoomTile_badge {
|
||||
letter-spacing: 0.1em;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mx_RoomTile.mx_RoomTile_noBadges .mx_RoomTile_badge.mx_RoomTile_badgeButton,
|
||||
.mx_RoomTile.mx_RoomTile_notificationStateMenu.mx_RoomTile_noBadges .mx_RoomTile_badge {
|
||||
background-color: rgb(214, 214, 214);
|
||||
}
|
||||
|
||||
.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: 800;
|
||||
}
|
||||
|
||||
.mx_RoomTile_selected {
|
||||
background-color: rgba(118,207,166,0.2);
|
||||
}
|
||||
|
||||
.mx_RoomTile .mx_RoomTile_name.mx_RoomTile_badgeShown {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.mx_RoomTile_arrow {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.mx_RoomTile:hover {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,47 +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_DevicesPanel {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 880px;
|
||||
border-spacing: 2px;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_header {
|
||||
display: table-header-group;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_header > div {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_header .mx_DevicesPanel_deviceLastSeen {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_header .mx_DevicesPanel_deviceButtons {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_device {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_DevicesPanel_device > div {
|
||||
display: table-cell;
|
||||
}
|
||||
@@ -1,33 +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_IntegrationsManager {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-justify-content: center;
|
||||
}
|
||||
|
||||
.mx_IntegrationsManager iframe {
|
||||
background-color: #fff;
|
||||
border: 0px;
|
||||
width: 720px;
|
||||
height: 512px;
|
||||
}
|
||||
@@ -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,11 +14,24 @@ 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;
|
||||
.mx_MImageTile_thumbnail {
|
||||
/*
|
||||
background-color: #fff;
|
||||
font-size: 15px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 1px;
|
||||
*/
|
||||
}
|
||||
|
||||
.mx_MImageTile_download {
|
||||
color: #80cef4;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MImageTile_download a {
|
||||
color: #80cef4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mx_MImageTile_download img {
|
||||
padding-right: 8px;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
/*
|
||||
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.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -11,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_UnknownBody {
|
||||
.mx_MTextTile {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
134
src/skins/vector/css/molecules/MemberTile.css
Normal file
134
src/skins/vector/css/molecules/MemberTile.css
Normal 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;
|
||||
}
|
||||
84
src/skins/vector/css/molecules/MessageComposer.css
Normal file
84
src/skins/vector/css/molecules/MessageComposer.css
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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_MessageComposer_wrapper {
|
||||
max-width: 720px;
|
||||
height: 50px;
|
||||
vertical-align: middle;
|
||||
margin: auto;
|
||||
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: 5px;
|
||||
padding-right: 10px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.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: 50px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_input textarea {
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
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;
|
||||
|
||||
/* needed for FF */
|
||||
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%;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_upload {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding-right: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_upload img {
|
||||
margin-top: 5px;
|
||||
}
|
||||
111
src/skins/vector/css/molecules/MessageTile.css
Normal file
111
src/skins/vector/css/molecules/MessageTile.css
Normal 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;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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,11 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_MTextBody {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mx_MTextBody pre{
|
||||
overflow-y: auto;
|
||||
max-height: 30vh;
|
||||
.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;
|
||||
}
|
||||
173
src/skins/vector/css/molecules/RoomHeader.css
Normal file
173
src/skins/vector/css/molecules/RoomHeader.css
Normal 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;
|
||||
}
|
||||
70
src/skins/vector/css/molecules/RoomSettings.css
Normal file
70
src/skins/vector/css/molecules/RoomSettings.css
Normal 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;
|
||||
}
|
||||
104
src/skins/vector/css/molecules/RoomTile.css
Normal file
104
src/skins/vector/css/molecules/RoomTile.css
Normal 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 {
|
||||
}
|
||||
@@ -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,19 +14,20 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_RoomTooltip_chevron {
|
||||
position: absolute;
|
||||
left: -9px;
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.mx_RoomTooltip {
|
||||
display: none;
|
||||
position: fixed;
|
||||
border: 1px solid #a4a4a4;
|
||||
border: 1px solid #a9dbf4;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
z-index: 2000;
|
||||
left: 52px;
|
||||
z-index: 1000;
|
||||
margin-top: 6px;
|
||||
left: 64px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.mx_RoomTooltip_chevron {
|
||||
position: absolute;
|
||||
left: -9px;
|
||||
top: 8px;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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 {
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,25 +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.collapsed .mx_BottomLeftMenu {
|
||||
flex: 0 0 120px;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu {
|
||||
@@ -62,45 +53,17 @@ limitations under the License.
|
||||
-webkit-order: 3;
|
||||
order: 3;
|
||||
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin-left: 16px; /* gutter */
|
||||
margin-right: 16px; /* gutter */
|
||||
-webkit-flex: 0 0 60px;
|
||||
flex: 0 0 60px;
|
||||
z-index: 1;
|
||||
-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.collapsed .mx_BottomLeftMenu_settings {
|
||||
float: none;
|
||||
}
|
||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
||||
margin-top: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -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,47 +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_content {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
.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_link
|
||||
{
|
||||
color: #fff ! important;
|
||||
text-decoration: underline ! important;
|
||||
cursor: pointer;
|
||||
.mx_MemberList_wrapper {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_close {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_close img {
|
||||
display: block;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_action {
|
||||
margin-right: 16px;
|
||||
.mx_MemberList h2 {
|
||||
margin: 14px;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user