fix conflicts
This commit is contained in:
commit
f9040e08ce
82 changed files with 2870 additions and 1037 deletions
|
@ -15,7 +15,18 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
.mx_MemberAvatar {
|
||||
z-index: 20;
|
||||
border-radius: 20px;
|
||||
/* commenting this out as it breaks on FF seemingly */
|
||||
/* position: relative; */
|
||||
}
|
||||
|
||||
.mx_MemberAvatar_initial {
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
speak: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_MemberAvatar_image {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
|
|
@ -14,22 +14,14 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
.mx_RoomAvatar {
|
||||
}
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var CasLoginController = require('matrix-react-sdk/lib/controllers/organisms/CasLogin');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CasLogin',
|
||||
mixins: [CasLoginController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.onCasClicked}>Sign in with CAS</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
.mx_RoomAvatar_initial {
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: normal ! important;
|
||||
speak: none;
|
||||
pointer-events: none;
|
||||
}
|
32
src/skins/vector/css/atoms/Spinner.css
Normal file
32
src/skins/vector/css/atoms/Spinner.css
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
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_Spinner {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-justify-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_middlePanel .mx_Spinner {
|
||||
height: auto;
|
||||
}
|
|
@ -47,6 +47,14 @@ a:visited {
|
|||
color: #76cfa6;
|
||||
}
|
||||
|
||||
/* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
|
||||
Stop the scrollbar view from pushing out the container's overall sizing, which causes
|
||||
flexbox to adapt to the new size and cause the view to keep growing.
|
||||
*/
|
||||
.gm-scrollbar-container .gm-scroll-view {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -91,19 +99,9 @@ a:visited {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.mx_Dialog_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
opacity: 0.2;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.mx_Dialog_wrapper {
|
||||
position: fixed;
|
||||
z-index: 4000;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
@ -124,7 +122,7 @@ a:visited {
|
|||
background-color: #fff;
|
||||
color: #747474;
|
||||
text-align: center;
|
||||
z-index: 2010;
|
||||
z-index: 4010;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
|
@ -132,6 +130,16 @@ a:visited {
|
|||
max-width: 80%;
|
||||
}
|
||||
|
||||
.mx_Dialog_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.mx_Dialog_lightbox .mx_Dialog_background {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
|
1
src/skins/vector/css/gemini-scrollbar.css
Symbolic link
1
src/skins/vector/css/gemini-scrollbar.css
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../node_modules/react-gemini-scrollbar/node_modules/gemini-scrollbar/gemini-scrollbar.css
|
1
src/skins/vector/css/gfm.css
Symbolic link
1
src/skins/vector/css/gfm.css
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../node_modules/gfm.css/gfm.css
|
1
src/skins/vector/css/github.css
Symbolic link
1
src/skins/vector/css/github.css
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../node_modules/highlight.js/styles/github.css
|
|
@ -1,4 +1,3 @@
|
|||
.mx_RoomDropTarget,
|
||||
.mx_RoomSettings_encrypt,
|
||||
.mx_CreateRoom_encrypt,
|
||||
.mx_RightPanel_filebutton
|
||||
|
|
|
@ -18,13 +18,13 @@ limitations under the License.
|
|||
max-width: 100%;
|
||||
clear: both;
|
||||
margin-top: 24px;
|
||||
margin-left: 56px;
|
||||
margin-left: 65px;
|
||||
}
|
||||
|
||||
.mx_EventTile_avatar {
|
||||
padding-left: 18px;
|
||||
padding-right: 12px;
|
||||
margin-left: -64px;
|
||||
margin-left: -73px;
|
||||
margin-top: -4px;
|
||||
float: left;
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ limitations under the License.
|
|||
.mx_EventTile .mx_MessageTimestamp {
|
||||
color: #acacac;
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_EventTile_line {
|
||||
|
@ -66,6 +65,28 @@ limitations under the License.
|
|||
margin-right: 100px;
|
||||
}
|
||||
|
||||
/* Various markdown overrides */
|
||||
|
||||
.mx_MessageTile_content .markdown-body {
|
||||
font-family: inherit ! important;
|
||||
white-space: normal ! important;
|
||||
line-height: inherit ! important;
|
||||
}
|
||||
|
||||
.mx_MessageTile_content .markdown-body h1,
|
||||
.mx_MessageTile_content .markdown-body h2,
|
||||
.mx_MessageTile_content .markdown-body h3,
|
||||
.mx_MessageTile_content .markdown-body h4,
|
||||
.mx_MessageTile_content .markdown-body h5,
|
||||
.mx_MessageTile_content .markdown-body h6
|
||||
{
|
||||
font-family: inherit ! important;
|
||||
}
|
||||
|
||||
.mx_MessageTile_content .markdown-body a {
|
||||
color: #76cfa6;
|
||||
}
|
||||
|
||||
.mx_MessageTile_searchHighlight {
|
||||
background-color: #76cfa6;
|
||||
color: #fff;
|
||||
|
@ -78,7 +99,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_EventTile_notSent {
|
||||
color: #f11;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.mx_EventTile_highlight {
|
||||
|
@ -91,10 +112,18 @@ limitations under the License.
|
|||
|
||||
.mx_EventTile_msgOption {
|
||||
float: right;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: 90px;
|
||||
margin-right: 10px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.mx_MessageTimestamp {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mx_EventTile_last .mx_MessageTimestamp {
|
||||
|
@ -107,9 +136,8 @@ limitations under the License.
|
|||
|
||||
.mx_EventTile_editButton {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: 15px;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.mx_EventTile:hover .mx_EventTile_editButton {
|
||||
|
@ -123,3 +151,21 @@ limitations under the License.
|
|||
.mx_EventTile.menu .mx_MessageTimestamp {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mx_EventTile_readAvatars {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.mx_EventTile_readAvatars .mx_MemberAvatar {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mx_EventTile_readAvatarRemainder {
|
||||
color: #acacac;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
@ -15,5 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
.mx_MNoticeTile {
|
||||
white-space: pre-wrap;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
|
|
@ -15,20 +15,40 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
.mx_MatrixToolbar {
|
||||
text-align: center;
|
||||
background-color: #ff0064;
|
||||
background-color: #76cfa6;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
padding: 6px;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar button {
|
||||
margin-left: 12px;
|
||||
.mx_MatrixToolbar_warning {
|
||||
margin-left: 16px;
|
||||
margin-right: 8px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_link
|
||||
{
|
||||
color: #fff ! important;
|
||||
text-decoration: underline ! important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_close {
|
||||
float: right;
|
||||
margin-top: 3px;
|
||||
margin-right: 12px;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mx_MatrixToolbar_close img {
|
||||
display: block;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
|
@ -16,29 +16,25 @@ limitations under the License.
|
|||
|
||||
.mx_MessageComposer_wrapper {
|
||||
max-width: 960px;
|
||||
height: 70px;
|
||||
vertical-align: middle;
|
||||
margin: auto;
|
||||
background-color: #fff;
|
||||
border-top: 2px solid #e1dddd;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_row {
|
||||
display: table-row;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer .mx_MessageComposer_avatar {
|
||||
display: table-cell;
|
||||
padding-left: 10px;
|
||||
padding-right: 20px;
|
||||
height: 70px;
|
||||
padding-right: 28px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_MessageComposer .mx_MessageComposer_avatar img {
|
||||
margin-top: 18px;
|
||||
border-radius: 20px;
|
||||
.mx_MessageComposer .mx_MessageComposer_avatar .mx_MemberAvatar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_input {
|
||||
|
@ -49,17 +45,18 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_MessageComposer_input textarea {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
height: 1.2em;
|
||||
padding-top: 0.7em;
|
||||
padding-bottom: 0.7em;
|
||||
padding: 0px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
border: 0px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
/* needed for FF */
|
||||
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
|
||||
|
@ -75,7 +72,8 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_MessageComposer_upload,
|
||||
.mx_MessageComposer_call {
|
||||
.mx_MessageComposer_voicecall,
|
||||
.mx_MessageComposer_videocall {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding-left: 10px;
|
||||
|
@ -83,7 +81,12 @@ limitations under the License.
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_call {
|
||||
.mx_MessageComposer_videocall {
|
||||
padding-right: 10px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_voicecall {
|
||||
padding-right: 10px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,46 @@ limitations under the License.
|
|||
|
||||
.mx_RoomDropTarget {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
background-color: #fbfbfb;
|
||||
border: 1px dashed #d7d7d7;
|
||||
border-radius: 8px;
|
||||
margin-left: 10px;
|
||||
margin-right: 15px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
border: 1px dashed #76cfa6;
|
||||
color: #454545;
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomDropTarget {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_RoomDropTarget_placeholder {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.mx_RoomDropTarget_avatar {
|
||||
background-color: #fff;
|
||||
border-radius: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
float: left;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.mx_RoomDropTarget_label {
|
||||
position: relative;
|
||||
margin-top: 3px;
|
||||
line-height: 21px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomDropTarget_avatar {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomDropTarget_label {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ limitations under the License.
|
|||
.mx_RoomHeader_leftRow {
|
||||
height: 48px;
|
||||
margin-top: 18px;
|
||||
margin-left: -2px;
|
||||
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
|
@ -89,9 +90,9 @@ limitations under the License.
|
|||
|
||||
.mx_RoomHeader_simpleHeader {
|
||||
line-height: 83px;
|
||||
color: #76cfa6;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
color: #454545;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@ -101,9 +102,9 @@ limitations under the License.
|
|||
vertical-align: middle;
|
||||
height: 28px;
|
||||
color: #454545;
|
||||
font-weight: 800;
|
||||
font-weight: bold;
|
||||
font-size: 24px;
|
||||
padding-left: 8px;
|
||||
padding-left: 19px;
|
||||
padding-right: 16px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ limitations under the License.
|
|||
max-height: 38px;
|
||||
color: #454545;
|
||||
font-weight: 300;
|
||||
padding-left: 8px;
|
||||
padding-left: 19px;
|
||||
padding-right: 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -16,13 +16,13 @@ limitations under the License.
|
|||
|
||||
.mx_RoomTile {
|
||||
cursor: pointer;
|
||||
display: table-row;
|
||||
/* This fixes wrapping of long room names, but breaks drag & drop previews */
|
||||
/* display: table-row; */
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mx_RoomTile_avatar {
|
||||
display: table-cell;
|
||||
background: #eaf5f0;
|
||||
padding-right: 8px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 2px;
|
||||
|
@ -39,17 +39,16 @@ limitations under the License.
|
|||
|
||||
.mx_RoomTile_name {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 16px;
|
||||
color: #454545;
|
||||
opacity: 0.8;
|
||||
color: rgba(69, 69, 69, 0.8);
|
||||
}
|
||||
|
||||
.mx_RoomTile_invite {
|
||||
opacity: 0.5;
|
||||
font-weight: normal;
|
||||
color: rgba(69, 69, 69, 0.5);
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomTile_name {
|
||||
|
@ -105,16 +104,15 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_RoomTile_unread,
|
||||
.mx_RoomTile_highlight,
|
||||
.mx_RoomTile_invited
|
||||
{
|
||||
.mx_RoomTile_highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mx_RoomTile_selected {
|
||||
.mx_RoomTile_selected .mx_RoomTile_name {
|
||||
color: #76cfa6 ! important;
|
||||
}
|
||||
|
||||
.mx_RoomTile.mx_RoomTile_selected {
|
||||
.mx_RoomTile.mx_RoomTile_selected .mx_RoomTile_name {
|
||||
background: url('img/selected.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
|
|
|
@ -21,7 +21,6 @@ limitations under the License.
|
|||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
z-index: 1000;
|
||||
margin-top: 6px;
|
||||
left: 64px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
|
|
@ -34,16 +34,21 @@ limitations under the License.
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_RoomList {
|
||||
.mx_LeftPanel_callView {
|
||||
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_RoomList_scrollbar {
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
overflow-y: auto;
|
||||
-webkit-flex: 1 1 0;
|
||||
flex: 1 1 0;
|
||||
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu {
|
||||
|
@ -53,8 +58,10 @@ limitations under the License.
|
|||
-webkit-order: 3;
|
||||
order: 3;
|
||||
|
||||
-webkit-flex: 0 0 126px;
|
||||
flex: 0 0 126px;
|
||||
-webkit-flex: 0 0 140px;
|
||||
flex: 0 0 140px;
|
||||
|
||||
background-color: rgba(118,207,166,0.19);
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile {
|
||||
|
@ -62,7 +69,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
||||
margin-top: 12px;
|
||||
margin-top: 17px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,7 @@ limitations under the License.
|
|||
|
||||
.mx_RoomList {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.mx_RoomList_invites,
|
||||
.mx_RoomList_recents {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.mx_RoomList_expandButton {
|
||||
|
@ -32,13 +26,9 @@ limitations under the License.
|
|||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.mx_RoomList h2 {
|
||||
text-transform: uppercase;
|
||||
color: #3d3b39;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
/* 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;
|
||||
}
|
||||
|
|
45
src/skins/vector/css/organisms/RoomSubList.css
Normal file
45
src/skins/vector/css/organisms/RoomSubList.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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_RoomSubList {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_bottommost {
|
||||
/* XXX: this should really be 100% of the RoomList height, but can't seem to get at it */
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_label {
|
||||
text-transform: uppercase;
|
||||
color: #3d3b39;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_chevron {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.collapsed .mx_RoomSubList_chevron {
|
||||
padding-left: 13px;
|
||||
}
|
|
@ -125,11 +125,11 @@ limitations under the License.
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.mx_RoomView_MessageList h2 {
|
||||
.mx_RoomView_MessageList > h2 {
|
||||
clear: both;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 54px;
|
||||
margin-left: 63px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
@ -158,18 +158,19 @@ limitations under the License.
|
|||
order: 4;
|
||||
|
||||
width: 100%;
|
||||
-webkit-flex: 0 0 36px;
|
||||
flex: 0 0 36px;
|
||||
-webkit-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mx_RoomView_statusAreaBox {
|
||||
max-width: 960px;
|
||||
margin: auto;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.mx_RoomView_statusAreaBox_line {
|
||||
border-top: 1px solid #eee;
|
||||
margin-left: 54px;
|
||||
margin-left: 63px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
|
@ -185,16 +186,44 @@ limitations under the License.
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_RoomView_connectionLostBar {
|
||||
margin-top: 19px;
|
||||
height: 58px;
|
||||
}
|
||||
|
||||
.mx_RoomView_connectionLostBar img {
|
||||
padding-left: 10px;
|
||||
padding-right: 22px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mx_RoomView_connectionLostBar_title {
|
||||
color: #ff0064;
|
||||
}
|
||||
|
||||
.mx_RoomView_connectionLostBar_desc {
|
||||
color: #454545;
|
||||
font-size: 14px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.mx_RoomView_resend_link {
|
||||
color: #454545 ! important;
|
||||
text-decoration: underline ! important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_RoomView_typingBar {
|
||||
margin-top: 10px;
|
||||
margin-left: 54px;
|
||||
margin-left: 63px;
|
||||
color: #4a4a4a;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.mx_RoomView_typingImage {
|
||||
display: inline;
|
||||
margin-left: -38px;
|
||||
margin-left: -47px;
|
||||
margin-top: -4px;
|
||||
float: left;
|
||||
}
|
||||
|
@ -207,14 +236,14 @@ limitations under the License.
|
|||
order: 5;
|
||||
|
||||
width: 100%;
|
||||
-webkit-flex: 0 0 70px;
|
||||
flex: 0 0 70px;
|
||||
-webkit-flex: 0;
|
||||
flex: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.mx_RoomView_uploadProgressOuter {
|
||||
height: 4px;
|
||||
margin-left: 54px;
|
||||
margin-left: 63px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
|
@ -225,7 +254,7 @@ limitations under the License.
|
|||
|
||||
.mx_RoomView_uploadFilename {
|
||||
margin-top: 5px;
|
||||
margin-left: 56px;
|
||||
margin-left: 65px;
|
||||
opacity: 0.5;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,18 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_MatrixChat_splash {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_splashButtons {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_wrapper {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
|
@ -35,7 +47,7 @@ limitations under the License.
|
|||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
height: 21px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_toolbarShowing {
|
||||
|
@ -71,8 +83,8 @@ limitations under the License.
|
|||
|
||||
background-color: #eaf5f0;
|
||||
|
||||
-webkit-flex: 0 0 230px;
|
||||
flex: 0 0 230px;
|
||||
-webkit-flex: 0 0 210px;
|
||||
flex: 0 0 210px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat .mx_LeftPanel.collapsed {
|
||||
|
@ -87,8 +99,8 @@ limitations under the License.
|
|||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-left: 25px;
|
||||
padding-right: 22px;
|
||||
background-color: #fff;
|
||||
|
||||
-webkit-flex: 1;
|
||||
|
@ -97,7 +109,8 @@ limitations under the License.
|
|||
/* XXX: Hack: apparently if you try to nest a flex-box
|
||||
* within a non-flex-box within a flex-box, the height
|
||||
* of the innermost element gets miscalculated if the
|
||||
* parents are both auto.
|
||||
* parents are both auto. Height has to be auto here
|
||||
* for RoomView to correctly fit when the Toolbar is shown.
|
||||
* Ideally we'd launch straight into the RoomView at this
|
||||
* point, but instead we fudge it and make the middlePanel
|
||||
* flex itself.
|
||||
|
@ -116,8 +129,8 @@ limitations under the License.
|
|||
-webkit-order: 3;
|
||||
order: 3;
|
||||
|
||||
-webkit-flex: 0 0 230px;
|
||||
flex: 0 0 230px;
|
||||
-webkit-flex: 0 0 235px;
|
||||
flex: 0 0 235px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat .mx_RightPanel.collapsed {
|
||||
|
|
|
@ -17,6 +17,18 @@ limitations under the License.
|
|||
.mx_Login {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mx_Login h2 {
|
||||
|
@ -28,8 +40,10 @@ limitations under the License.
|
|||
|
||||
.mx_Login_box {
|
||||
width: 300px;
|
||||
min-height: 450px;
|
||||
padding-top: 50px;
|
||||
padding-bottom: 50px;
|
||||
margin: auto;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.mx_Login_logo {
|
||||
|
|
BIN
src/skins/vector/img/cancel-black2.png
Normal file
BIN
src/skins/vector/img/cancel-black2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
src/skins/vector/img/list-close.png
Normal file
BIN
src/skins/vector/img/list-close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
BIN
src/skins/vector/img/list-open.png
Normal file
BIN
src/skins/vector/img/list-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
BIN
src/skins/vector/img/voice.png
Normal file
BIN
src/skins/vector/img/voice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 856 B |
BIN
src/skins/vector/img/warning.png
Normal file
BIN
src/skins/vector/img/warning.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
src/skins/vector/img/warning2.png
Normal file
BIN
src/skins/vector/img/warning2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -65,13 +65,11 @@ skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
|
|||
skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip');
|
||||
skin['molecules.SearchBar'] = require('./views/molecules/SearchBar');
|
||||
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
|
||||
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
|
||||
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
|
||||
skin['molecules.UserSelector'] = require('./views/molecules/UserSelector');
|
||||
skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView');
|
||||
skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox');
|
||||
skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView');
|
||||
skin['organisms.CasLogin'] = require('./views/organisms/CasLogin');
|
||||
skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom');
|
||||
skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog');
|
||||
skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel');
|
||||
|
@ -82,12 +80,11 @@ skin['organisms.QuestionDialog'] = require('./views/organisms/QuestionDialog');
|
|||
skin['organisms.RightPanel'] = require('./views/organisms/RightPanel');
|
||||
skin['organisms.RoomDirectory'] = require('./views/organisms/RoomDirectory');
|
||||
skin['organisms.RoomList'] = require('./views/organisms/RoomList');
|
||||
skin['organisms.RoomSubList'] = require('./views/organisms/RoomSubList');
|
||||
skin['organisms.RoomView'] = require('./views/organisms/RoomView');
|
||||
skin['organisms.UserSettings'] = require('./views/organisms/UserSettings');
|
||||
skin['organisms.ViewSource'] = require('./views/organisms/ViewSource');
|
||||
skin['pages.CompatibilityPage'] = require('./views/pages/CompatibilityPage');
|
||||
skin['pages.MatrixChat'] = require('./views/pages/MatrixChat');
|
||||
skin['templates.Login'] = require('./views/templates/Login');
|
||||
skin['templates.Register'] = require('./views/templates/Register');
|
||||
|
||||
module.exports = skin;
|
|
@ -40,10 +40,32 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: recalculates default avatar url constantly
|
||||
if (this.state.imageUrl === this.defaultAvatarUrl(this.props.member)) {
|
||||
var initial;
|
||||
if (this.props.member.name[0])
|
||||
initial = this.props.member.name[0].toUpperCase();
|
||||
if (initial === '@' && this.props.member.name[1])
|
||||
initial = this.props.member.name[1].toUpperCase();
|
||||
|
||||
return (
|
||||
<span className="mx_MemberAvatar" {...this.props}>
|
||||
<span className="mx_MemberAvatar_initial" aria-hidden="true"
|
||||
style={{ fontSize: (this.props.width * 0.75) + "px",
|
||||
width: this.props.width + "px",
|
||||
lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>
|
||||
<img className="mx_MemberAvatar_image" src={this.state.imageUrl} title={this.props.member.name}
|
||||
onError={this.onError} width={this.props.width} height={this.props.height} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<img className="mx_MemberAvatar" src={this.state.imageUrl}
|
||||
<img className="mx_MemberAvatar mx_MemberAvatar_image" src={this.state.imageUrl}
|
||||
onError={this.onError}
|
||||
width={this.props.width} height={this.props.height} />
|
||||
width={this.props.width} height={this.props.height}
|
||||
title={this.props.member.name}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -43,13 +43,33 @@ module.exports = React.createClass({
|
|||
|
||||
render: function() {
|
||||
var style = {
|
||||
maxWidth: this.props.width,
|
||||
maxHeight: this.props.height,
|
||||
width: this.props.width,
|
||||
height: this.props.height,
|
||||
};
|
||||
return (
|
||||
<img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
|
||||
// XXX: recalculates fallback avatar constantly
|
||||
if (this.state.imageUrl === this.getFallbackAvatar()) {
|
||||
var initial;
|
||||
if (this.props.room.name[0])
|
||||
initial = this.props.room.name[0].toUpperCase();
|
||||
if ((initial === '@' || initial === '#') && this.props.room.name[1])
|
||||
initial = this.props.room.name[1].toUpperCase();
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span className="mx_RoomAvatar_initial" aria-hidden="true"
|
||||
style={{ fontSize: (this.props.width * 0.75) + "px",
|
||||
width: this.props.width + "px",
|
||||
lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>
|
||||
<img className="mx_RoomAvatar" src={this.state.imageUrl}
|
||||
onError={this.onError} style={style} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return <img className="mx_RoomAvatar" src={this.state.imageUrl}
|
||||
onError={this.onError} style={style} />
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ module.exports = React.createClass({
|
|||
var h = this.props.h || 32;
|
||||
var imgClass = this.props.imgClassName || "";
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_Spinner">
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -21,9 +21,6 @@ var React = require('react');
|
|||
var sdk = require('matrix-react-sdk')
|
||||
var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangeAvatar',
|
||||
mixins: [ChangeAvatarController],
|
||||
|
@ -70,6 +67,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
case this.Phases.Uploading:
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
|
|
|
@ -20,8 +20,6 @@ var React = require('react');
|
|||
var sdk = require('matrix-react-sdk');
|
||||
|
||||
var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName");
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangeDisplayName',
|
||||
|
@ -39,6 +37,7 @@ module.exports = React.createClass({
|
|||
|
||||
render: function() {
|
||||
if (this.state.busy) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
|
|
|
@ -19,17 +19,15 @@ limitations under the License.
|
|||
var React = require('react');
|
||||
|
||||
var ChangePasswordController = require('matrix-react-sdk/lib/controllers/molecules/ChangePassword')
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangePassword',
|
||||
mixins: [ChangePasswordController],
|
||||
|
||||
onClickChange: function() {
|
||||
var old_password = this.refs.old_input.getDOMNode().value;
|
||||
var new_password = this.refs.new_input.getDOMNode().value;
|
||||
var confirm_password = this.refs.confirm_input.getDOMNode().value;
|
||||
var old_password = this.refs.old_input.value;
|
||||
var new_password = this.refs.new_input.value;
|
||||
var confirm_password = this.refs.confirm_input.value;
|
||||
if (new_password != confirm_password) {
|
||||
this.setState({
|
||||
state: this.Phases.Error,
|
||||
|
@ -64,6 +62,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
case this.Phases.Uploading:
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<div className="mx_Dialog_content">
|
||||
<Loader />
|
||||
|
|
|
@ -17,15 +17,28 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDom = require('react-dom');
|
||||
var classNames = require("classnames");
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
|
||||
|
||||
var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
|
||||
var ContextualMenu = require('../../../../ContextualMenu');
|
||||
|
||||
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
||||
|
||||
var Velociraptor = require('../../../../Velociraptor');
|
||||
require('../../../../VelocityBounce');
|
||||
|
||||
var bounce = false;
|
||||
try {
|
||||
if (global.localStorage) {
|
||||
bounce = global.localStorage.getItem('avatar_bounce') == 'true';
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
var eventTileTypes = {
|
||||
'm.room.message': 'molecules.MessageTile',
|
||||
'm.room.member' : 'molecules.EventAsTextTile',
|
||||
|
@ -36,6 +49,8 @@ var eventTileTypes = {
|
|||
'm.room.topic' : 'molecules.EventAsTextTile',
|
||||
};
|
||||
|
||||
var MAX_READ_AVATARS = 5;
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'EventTile',
|
||||
mixins: [EventTileController],
|
||||
|
@ -52,7 +67,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {menu: false};
|
||||
return {menu: false, allReadAvatars: false};
|
||||
},
|
||||
|
||||
onEditClicked: function(e) {
|
||||
|
@ -72,6 +87,127 @@ module.exports = React.createClass({
|
|||
this.setState({menu: true});
|
||||
},
|
||||
|
||||
toggleAllReadAvatars: function() {
|
||||
this.setState({
|
||||
allReadAvatars: !this.state.allReadAvatars
|
||||
});
|
||||
},
|
||||
|
||||
getReadAvatars: function() {
|
||||
var avatars = [];
|
||||
|
||||
var room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
|
||||
if (!room) return [];
|
||||
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
|
||||
// get list of read receipts, sorted most recent first
|
||||
var receipts = room.getReceiptsForEvent(this.props.mxEvent).filter(function(r) {
|
||||
return r.type === "m.read" && r.userId != myUserId;
|
||||
}).sort(function(r1, r2) {
|
||||
return r2.data.ts - r1.data.ts;
|
||||
});
|
||||
|
||||
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
|
||||
|
||||
var left = 0;
|
||||
|
||||
var reorderTransitionOpts = {
|
||||
duration: 100,
|
||||
easing: 'easeOut'
|
||||
};
|
||||
|
||||
for (var i = 0; i < receipts.length; ++i) {
|
||||
var member = room.getMember(receipts[i].userId);
|
||||
|
||||
// Using react refs here would mean both getting Velociraptor to expose
|
||||
// them and making them scoped to the whole RoomView. Not impossible, but
|
||||
// getElementById seems simpler at least for a first cut.
|
||||
var oldAvatarDomNode = document.getElementById('mx_readAvatar'+member.userId);
|
||||
var startStyles = [];
|
||||
var enterTransitionOpts = [];
|
||||
var oldNodeTop = -15; // For avatars that weren't on screen, act as if they were just off the top
|
||||
if (oldAvatarDomNode) {
|
||||
oldNodeTop = oldAvatarDomNode.getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
if (this.readAvatarNode) {
|
||||
var topOffset = oldNodeTop - this.readAvatarNode.getBoundingClientRect().top;
|
||||
|
||||
if (oldAvatarDomNode && oldAvatarDomNode.style.left !== '0px') {
|
||||
var leftOffset = oldAvatarDomNode.style.left;
|
||||
// start at the old height and in the old h pos
|
||||
startStyles.push({ top: topOffset, left: leftOffset });
|
||||
enterTransitionOpts.push(reorderTransitionOpts);
|
||||
}
|
||||
|
||||
// then shift to the rightmost column,
|
||||
// and then it will drop down to its resting position
|
||||
startStyles.push({ top: topOffset, left: '0px' });
|
||||
enterTransitionOpts.push({
|
||||
duration: bounce ? Math.min(Math.log(Math.abs(topOffset)) * 200, 3000) : 300,
|
||||
easing: bounce ? 'easeOutBounce' : 'easeOutCubic',
|
||||
});
|
||||
}
|
||||
|
||||
var style = {
|
||||
left: left+'px',
|
||||
top: '0px',
|
||||
visibility: ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) ? 'visible' : 'hidden'
|
||||
};
|
||||
|
||||
//console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility);
|
||||
|
||||
// add to the start so the most recent is on the end (ie. ends up rightmost)
|
||||
avatars.unshift(
|
||||
<MemberAvatar key={member.userId} member={member}
|
||||
width={14} height={14} resizeMethod="crop"
|
||||
style={style}
|
||||
startStyle={startStyles}
|
||||
enterTransitionOpts={enterTransitionOpts}
|
||||
id={'mx_readAvatar'+member.userId}
|
||||
onClick={this.toggleAllReadAvatars}
|
||||
/>
|
||||
);
|
||||
// TODO: we keep the extra read avatars in the dom to make animation simpler
|
||||
// we could optimise this to reduce the dom size.
|
||||
if (i < MAX_READ_AVATARS - 1 || this.state.allReadAvatars) { // XXX: where does this -1 come from? is it to make the max'th avatar animate properly?
|
||||
left -= 15;
|
||||
}
|
||||
}
|
||||
var editButton;
|
||||
if (!this.state.allReadAvatars) {
|
||||
var remainder = receipts.length - MAX_READ_AVATARS;
|
||||
var remText;
|
||||
if (i >= MAX_READ_AVATARS - 1) left -= 15;
|
||||
if (remainder > 0) {
|
||||
remText = <span className="mx_EventTile_readAvatarRemainder"
|
||||
onClick={this.toggleAllReadAvatars}
|
||||
style={{ left: left }}>{ remainder }+
|
||||
</span>;
|
||||
left -= 15;
|
||||
}
|
||||
editButton = (
|
||||
<input style={{ left: left }}
|
||||
type="image" src="img/edit.png" alt="Options" title="Options" width="14" height="14"
|
||||
className="mx_EventTile_editButton" onClick={this.onEditClicked} />
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="mx_EventTile_readAvatars" ref={this.collectReadAvatarNode}>
|
||||
{ editButton }
|
||||
{ remText }
|
||||
<Velociraptor transition={ reorderTransitionOpts }>
|
||||
{ avatars }
|
||||
</Velociraptor>
|
||||
</span>;
|
||||
},
|
||||
|
||||
collectReadAvatarNode: function(node) {
|
||||
this.readAvatarNode = ReactDom.findDOMNode(node);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
|
||||
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
|
||||
|
@ -100,18 +236,14 @@ module.exports = React.createClass({
|
|||
menu: this.state.menu,
|
||||
});
|
||||
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
var editButton = (
|
||||
<input
|
||||
type="image" src="img/edit.png" alt="Edit" width="14" height="14"
|
||||
className="mx_EventTile_editButton" onClick={this.onEditClicked}
|
||||
/>
|
||||
);
|
||||
|
||||
var aux = null;
|
||||
if (msgtype === 'm.image') aux = "sent an image";
|
||||
else if (msgtype === 'm.video') aux = "sent a video";
|
||||
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||
|
||||
var readAvatars = this.getReadAvatars();
|
||||
|
||||
var avatar, sender;
|
||||
if (!this.props.continuation) {
|
||||
if (this.props.mxEvent.sender) {
|
||||
|
@ -127,11 +259,13 @@ module.exports = React.createClass({
|
|||
}
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="mx_EventTile_msgOption">
|
||||
{ timestamp }
|
||||
{ readAvatars }
|
||||
</div>
|
||||
{ avatar }
|
||||
{ sender }
|
||||
<div className="mx_EventTile_line">
|
||||
{ timestamp }
|
||||
{ editButton }
|
||||
<EventTileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,15 +30,25 @@ module.exports = React.createClass({
|
|||
var content = this.props.mxEvent.getContent();
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
return (
|
||||
<span className="mx_MFileTile">
|
||||
<div className="mx_MImageTile_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<img src="img/download.png" width="10" height="12"/>
|
||||
Download {this.presentableTextForFile(content)}
|
||||
</a>
|
||||
</div>
|
||||
var httpUrl = cli.mxcUrlToHttp(content.url);
|
||||
var text = this.presentableTextForFile(content);
|
||||
|
||||
if (httpUrl) {
|
||||
return (
|
||||
<span className="mx_MFileTile">
|
||||
<div className="mx_MImageTile_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<img src="img/download.png" width="10" height="12"/>
|
||||
Download {text}
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
var extra = text ? ': '+text : '';
|
||||
return <span className="mx_MFileTile">
|
||||
Invalid file{extra}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -63,6 +63,34 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_isGif: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return (content && content.info && content.info.mimetype === "image/gif");
|
||||
},
|
||||
|
||||
onImageEnter: function(e) {
|
||||
if (!this._isGif()) {
|
||||
return;
|
||||
}
|
||||
var imgElement = e.target;
|
||||
imgElement.src = MatrixClientPeg.get().mxcUrlToHttp(
|
||||
this.props.mxEvent.getContent().url
|
||||
);
|
||||
},
|
||||
|
||||
onImageLeave: function(e) {
|
||||
if (!this._isGif()) {
|
||||
return;
|
||||
}
|
||||
var imgElement = e.target;
|
||||
imgElement.src = this._getThumbUrl();
|
||||
},
|
||||
|
||||
_getThumbUrl: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return MatrixClientPeg.get().mxcUrlToHttp(content.url, 480, 360);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
@ -73,18 +101,36 @@ module.exports = React.createClass({
|
|||
var imgStyle = {};
|
||||
if (thumbHeight) imgStyle['height'] = thumbHeight;
|
||||
|
||||
return (
|
||||
<span className="mx_MImageTile">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
|
||||
<img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 480, 360)} alt={content.body} style={imgStyle} />
|
||||
</a>
|
||||
<div className="mx_MImageTile_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<img src="img/download.png" width="10" height="12"/>
|
||||
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
|
||||
var thumbUrl = this._getThumbUrl();
|
||||
if (thumbUrl) {
|
||||
return (
|
||||
<span className="mx_MImageTile">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
|
||||
<img className="mx_MImageTile_thumbnail" src={thumbUrl}
|
||||
alt={content.body} style={imgStyle}
|
||||
onMouseEnter={this.onImageEnter}
|
||||
onMouseLeave={this.onImageLeave} />
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
<div className="mx_MImageTile_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<img src="img/download.png" width="10" height="12"/>
|
||||
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
} else if (content.body) {
|
||||
return (
|
||||
<span className="mx_MImageTile">
|
||||
Image '{content.body}' cannot be displayed.
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className="mx_MImageTile">
|
||||
This image cannot be displayed.
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -17,67 +17,34 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sanitizeHtml = require('sanitize-html');
|
||||
var HtmlUtils = require('../../../../HtmlUtils');
|
||||
|
||||
var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile')
|
||||
|
||||
var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
|
||||
allowedAttributes['font'] = ['color'];
|
||||
var sanitizeHtmlParams = {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
|
||||
allowedAttributes: allowedAttributes,
|
||||
};
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MNoticeTile',
|
||||
mixins: [MNoticeTileController],
|
||||
|
||||
// FIXME: this entire class is copy-pasted from MTextTile :(
|
||||
componentDidMount: function() {
|
||||
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||
HtmlUtils.highlightDom(this.getDOMNode());
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||
HtmlUtils.highlightDom(this.getDOMNode());
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps) {
|
||||
// exploit that events are immutable :)
|
||||
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
|
||||
nextProps.searchTerm !== this.props.searchTerm);
|
||||
},
|
||||
|
||||
// XXX: fix horrible duplication with MTextTile
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var originalBody = content.body;
|
||||
var body;
|
||||
|
||||
if (this.props.searchTerm) {
|
||||
var lastOffset = 0;
|
||||
var bodyList = [];
|
||||
var k = 0;
|
||||
var offset;
|
||||
|
||||
// XXX: rather than searching for the search term in the body,
|
||||
// we should be looking at the match delimiters returned by the FTS engine
|
||||
if (content.format === "org.matrix.custom.html") {
|
||||
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||
var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
|
||||
while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
|
||||
// FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
|
||||
// hooking into the sanitizer parser rather than treating it as a string. Otherwise
|
||||
// the act of highlighting a <b/> or whatever will break the HTML badly.
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
|
||||
lastOffset = offset + safeSearchTerm.length;
|
||||
}
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
|
||||
}
|
||||
else {
|
||||
while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
|
||||
bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
|
||||
bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
|
||||
lastOffset = offset + this.props.searchTerm.length;
|
||||
}
|
||||
bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
|
||||
}
|
||||
body = bodyList;
|
||||
}
|
||||
else {
|
||||
if (content.format === "org.matrix.custom.html") {
|
||||
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||
body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
|
||||
}
|
||||
else {
|
||||
body = originalBody;
|
||||
}
|
||||
}
|
||||
var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm);
|
||||
|
||||
return (
|
||||
<span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
|
||||
|
|
|
@ -17,67 +17,33 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sanitizeHtml = require('sanitize-html');
|
||||
var HtmlUtils = require('../../../../HtmlUtils');
|
||||
|
||||
var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile')
|
||||
|
||||
var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
|
||||
allowedAttributes['font'] = ['color'];
|
||||
var sanitizeHtmlParams = {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
|
||||
allowedAttributes: allowedAttributes,
|
||||
};
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MTextTile',
|
||||
mixins: [MTextTileController],
|
||||
|
||||
// FIXME: this entire class is copy-pasted from MTextTile :(
|
||||
componentDidMount: function() {
|
||||
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||
HtmlUtils.highlightDom(this.getDOMNode());
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||
HtmlUtils.highlightDom(this.getDOMNode());
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps) {
|
||||
// exploit that events are immutable :)
|
||||
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
|
||||
nextProps.searchTerm !== this.props.searchTerm);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var originalBody = content.body;
|
||||
var body;
|
||||
|
||||
if (this.props.searchTerm) {
|
||||
var lastOffset = 0;
|
||||
var bodyList = [];
|
||||
var k = 0;
|
||||
var offset;
|
||||
|
||||
// XXX: rather than searching for the search term in the body,
|
||||
// we should be looking at the match delimiters returned by the FTS engine
|
||||
if (content.format === "org.matrix.custom.html") {
|
||||
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||
var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
|
||||
while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
|
||||
// FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
|
||||
// hooking into the sanitizer parser rather than treating it as a string. Otherwise
|
||||
// the act of highlighting a <b/> or whatever will break the HTML badly.
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
|
||||
lastOffset = offset + safeSearchTerm.length;
|
||||
}
|
||||
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
|
||||
}
|
||||
else {
|
||||
while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
|
||||
bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
|
||||
bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
|
||||
lastOffset = offset + this.props.searchTerm.length;
|
||||
}
|
||||
bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
|
||||
}
|
||||
body = bodyList;
|
||||
}
|
||||
else {
|
||||
if (content.format === "org.matrix.custom.html") {
|
||||
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||
body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
|
||||
}
|
||||
else {
|
||||
body = originalBody;
|
||||
}
|
||||
}
|
||||
var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm);
|
||||
|
||||
return (
|
||||
<span ref="content" className="mx_MTextTile mx_MessageTile_content">
|
||||
|
|
|
@ -28,12 +28,19 @@ module.exports = React.createClass({
|
|||
Notifier.setToolbarHidden(true);
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
var Notifier = sdk.getComponent('organisms.Notifier');
|
||||
Notifier.setEnabled(true);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var EnableNotificationsButton = sdk.getComponent("atoms.EnableNotificationsButton");
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
You are not receiving desktop notifications. <EnableNotificationsButton />
|
||||
<div className="mx_MatrixToolbar_close"><img src="img/close-white.png" width="16" height="16" onClick={ this.hideToolbar } /></div>
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.png" width="28" height="28" alt="/!\"/>
|
||||
<div>
|
||||
You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a>
|
||||
</div>
|
||||
<div className="mx_MatrixToolbar_close"><img src="img/cancel-black2.png" width="23" height="23" onClick={ this.hideToolbar } /></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var Loader = require("../atoms/Spinner");
|
||||
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
@ -47,6 +46,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
if (this.state.creatingRoom) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,12 @@ module.exports = React.createClass({
|
|||
displayName: 'MessageComposer',
|
||||
mixins: [MessageComposerController],
|
||||
|
||||
onInputClick: function(ev) {
|
||||
this.refs.textarea.focus();
|
||||
},
|
||||
|
||||
onUploadClick: function(ev) {
|
||||
this.refs.uploadInput.getDOMNode().click();
|
||||
this.refs.uploadInput.click();
|
||||
},
|
||||
|
||||
onUploadFileSelected: function(ev) {
|
||||
|
@ -38,7 +42,7 @@ module.exports = React.createClass({
|
|||
if (files && files.length > 0) {
|
||||
this.props.uploadFile(files[0]);
|
||||
}
|
||||
this.refs.uploadInput.getDOMNode().value = null;
|
||||
this.refs.uploadInput.value = null;
|
||||
},
|
||||
|
||||
onCallClick: function(ev) {
|
||||
|
@ -49,6 +53,14 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onVoiceCallClick: function(ev) {
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: 'voice',
|
||||
room_id: this.props.room.roomId
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||
var uploadInputStyle = {display: 'none'};
|
||||
|
@ -60,15 +72,18 @@ module.exports = React.createClass({
|
|||
<div className="mx_MessageComposer_avatar">
|
||||
<MemberAvatar member={me} width={24} height={24} />
|
||||
</div>
|
||||
<div className="mx_MessageComposer_input">
|
||||
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message..." />
|
||||
<div className="mx_MessageComposer_input" onClick={ this.onInputClick }>
|
||||
<textarea ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder="Type a message..." />
|
||||
</div>
|
||||
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
|
||||
<img src="img/upload.png" width="17" height="22"/>
|
||||
<img src="img/upload.png" alt="Upload file" title="Upload file" width="17" height="22"/>
|
||||
<input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
|
||||
</div>
|
||||
<div className="mx_MessageComposer_call" onClick={this.onCallClick}>
|
||||
<img src="img/call.png" width="28" height="20"/>
|
||||
<div className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick}>
|
||||
<img src="img/voice.png" alt="Voice call" title="Voice call" width="16" height="26"/>
|
||||
</div>
|
||||
<div className="mx_MessageComposer_videocall" onClick={this.onCallClick}>
|
||||
<img src="img/call.png" alt="Video call" title="Video call" width="28" height="20"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,25 +22,13 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
|||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
var Resend = require("../../../../Resend");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageContextMenu',
|
||||
|
||||
onResendClick: function() {
|
||||
MatrixClientPeg.get().resendEvent(
|
||||
this.props.mxEvent, MatrixClientPeg.get().getRoom(
|
||||
this.props.mxEvent.getRoomId()
|
||||
)
|
||||
).done(function() {
|
||||
dis.dispatch({
|
||||
action: 'message_sent'
|
||||
});
|
||||
}, function() {
|
||||
dis.dispatch({
|
||||
action: 'message_send_failed'
|
||||
});
|
||||
});
|
||||
dis.dispatch({action: 'message_resend_started'});
|
||||
Resend.resend(this.props.mxEvent);
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
|
|
|
@ -18,16 +18,25 @@ limitations under the License.
|
|||
|
||||
var React = require('react');
|
||||
|
||||
//var RoomDropTargetController = require('matrix-react-sdk/lib/controllers/molecules/RoomDropTargetController')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomDropTarget',
|
||||
// mixins: [RoomDropTargetController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_RoomDropTarget">
|
||||
{this.props.text}
|
||||
</div>
|
||||
);
|
||||
if (this.props.placeholder) {
|
||||
return (
|
||||
<div className="mx_RoomDropTarget mx_RoomDropTarget_placeholder">
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="mx_RoomDropTarget">
|
||||
<div className="mx_RoomDropTarget_avatar"></div>
|
||||
<div className="mx_RoomDropTarget_label">
|
||||
{ this.props.label }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getRoomName: function() {
|
||||
return this.refs.name_edit.getDOMNode().value;
|
||||
return this.refs.name_edit.value;
|
||||
},
|
||||
|
||||
onFullscreenClick: function() {
|
||||
|
|
|
@ -27,15 +27,15 @@ module.exports = React.createClass({
|
|||
mixins: [RoomSettingsController],
|
||||
|
||||
getTopic: function() {
|
||||
return this.refs.topic.getDOMNode().value;
|
||||
return this.refs.topic.value;
|
||||
},
|
||||
|
||||
getJoinRules: function() {
|
||||
return this.refs.is_private.getDOMNode().checked ? "invite" : "public";
|
||||
return this.refs.is_private.checked ? "invite" : "public";
|
||||
},
|
||||
|
||||
getHistoryVisibility: function() {
|
||||
return this.refs.share_history.getDOMNode().checked ? "shared" : "invited";
|
||||
return this.refs.share_history.checked ? "shared" : "invited";
|
||||
},
|
||||
|
||||
getPowerLevels: function() {
|
||||
|
@ -45,13 +45,13 @@ module.exports = React.createClass({
|
|||
power_levels = power_levels.getContent();
|
||||
|
||||
var new_power_levels = {
|
||||
ban: parseInt(this.refs.ban.getDOMNode().value),
|
||||
kick: parseInt(this.refs.kick.getDOMNode().value),
|
||||
redact: parseInt(this.refs.redact.getDOMNode().value),
|
||||
invite: parseInt(this.refs.invite.getDOMNode().value),
|
||||
events_default: parseInt(this.refs.events_default.getDOMNode().value),
|
||||
state_default: parseInt(this.refs.state_default.getDOMNode().value),
|
||||
users_default: parseInt(this.refs.users_default.getDOMNode().value),
|
||||
ban: parseInt(this.refs.ban.value),
|
||||
kick: parseInt(this.refs.kick.value),
|
||||
redact: parseInt(this.refs.redact.value),
|
||||
invite: parseInt(this.refs.invite.value),
|
||||
events_default: parseInt(this.refs.events_default.value),
|
||||
state_default: parseInt(this.refs.state_default.value),
|
||||
users_default: parseInt(this.refs.users_default.value),
|
||||
users: power_levels.users,
|
||||
events: power_levels.events,
|
||||
};
|
||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var DragSource = require('react-dnd').DragSource;
|
||||
var DropTarget = require('react-dnd').DropTarget;
|
||||
var classNames = require('classnames');
|
||||
|
||||
var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile')
|
||||
|
@ -25,10 +27,179 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
|||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
module.exports = React.createClass({
|
||||
/**
|
||||
* 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) {
|
||||
// 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("organisms.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("organisms.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();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var RoomTile = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
mixins: [RoomTileController],
|
||||
|
||||
propTypes: {
|
||||
connectDragSource: React.PropTypes.func.isRequired,
|
||||
connectDropTarget: React.PropTypes.func.isRequired,
|
||||
isDragging: React.PropTypes.bool.isRequired,
|
||||
room: React.PropTypes.object.isRequired,
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
selected: React.PropTypes.bool.isRequired,
|
||||
unread: React.PropTypes.bool.isRequired,
|
||||
highlight: React.PropTypes.bool.isRequired,
|
||||
isInvite: React.PropTypes.bool.isRequired,
|
||||
roomSubList: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return( { hover : false });
|
||||
},
|
||||
|
@ -42,21 +213,34 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
// if (this.props.clientOffset) {
|
||||
// //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y);
|
||||
// }
|
||||
|
||||
/*
|
||||
if (this.props.room._dragging) {
|
||||
var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget");
|
||||
return <RoomDropTarget placeholder={true}/>;
|
||||
}
|
||||
*/
|
||||
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var me = this.props.room.currentState.members[myUserId];
|
||||
var classes = classNames({
|
||||
'mx_RoomTile': true,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_highlight': this.props.highlight,
|
||||
'mx_RoomTile_invited': this.props.room.currentState.members[myUserId].membership == 'invite'
|
||||
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
||||
});
|
||||
|
||||
var name;
|
||||
if (this.props.isInvite) {
|
||||
name = this.props.room.getMember(MatrixClientPeg.get().credentials.userId).events.member.getSender();
|
||||
name = this.props.room.getMember(myUserId).events.member.getSender();
|
||||
}
|
||||
else {
|
||||
name = this.props.room.name;
|
||||
// XXX: We should never display raw room IDs, but sometimes the room name js sdk gives is undefined
|
||||
name = this.props.room.name || this.props.room.roomId;
|
||||
}
|
||||
|
||||
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
||||
|
@ -91,7 +275,14 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
||||
return (
|
||||
|
||||
// These props are injected by React DnD,
|
||||
// as defined by your `collect` function above:
|
||||
var isDragging = this.props.isDragging;
|
||||
var connectDragSource = this.props.connectDragSource;
|
||||
var connectDropTarget = this.props.connectDropTarget;
|
||||
|
||||
return connectDragSource(connectDropTarget(
|
||||
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<RoomAvatar room={this.props.room} width="24" height="24" />
|
||||
|
@ -99,6 +290,27 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
{ label }
|
||||
</div>
|
||||
);
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// 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));
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
|
@ -24,21 +25,21 @@ module.exports = React.createClass({
|
|||
displayName: 'RoomTooltip',
|
||||
|
||||
componentDidMount: function() {
|
||||
var tooltip = ReactDOM.findDOMNode(this);
|
||||
if (!this.props.bottom) {
|
||||
// tell the roomlist about us so it can position us
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: this.getDOMNode(),
|
||||
tooltip: tooltip,
|
||||
});
|
||||
}
|
||||
else {
|
||||
var tooltip = this.getDOMNode();
|
||||
tooltip.style.top = tooltip.parentElement.getBoundingClientRect().top + "px";
|
||||
tooltip.style.display = "block";
|
||||
}
|
||||
},
|
||||
|
||||
componentDidUnmount: function() {
|
||||
componentWillUnmount: function() {
|
||||
if (!this.props.bottom) {
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
|
|
|
@ -39,7 +39,7 @@ module.exports = React.createClass({
|
|||
|
||||
onSearchChange: function(e) {
|
||||
if (e.keyCode === 13) { // on enter...
|
||||
this.props.onSearch(this.refs.search_term.getDOMNode().value, this.state.scope);
|
||||
this.props.onSearch(this.refs.search_term.value, this.state.scope);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
var ServerConfigController = require('matrix-react-sdk/lib/controllers/molecules/ServerConfig')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ServerConfig',
|
||||
mixins: [ServerConfigController],
|
||||
|
||||
showHelpPopup: function() {
|
||||
var ErrorDialog = sdk.getComponent('organisms.ErrorDialog');
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: 'Custom Server Options',
|
||||
description: "You can use the custom server options to log into other Matrix servers by specifying a different Home server URL. This allows you to use Vector with an existing Matrix account on a different Home server. You can also set a cutom Identity server but this will affect people ability to find you if you use a server in a group other than tha main Matrix.org group.",
|
||||
button: "Dismiss",
|
||||
focus: true
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ServerConfig">
|
||||
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label>
|
||||
<input className="mx_Login_field" id="hsurl" type="text" value={this.state.hs_url} onChange={this.hsChanged} />
|
||||
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label>
|
||||
<input className="mx_Login_field" type="text" value={this.state.is_url} onChange={this.isChanged} />
|
||||
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -25,8 +25,8 @@ module.exports = React.createClass({
|
|||
mixins: [UserSelectorController],
|
||||
|
||||
onAddUserId: function() {
|
||||
this.addUser(this.refs.user_id_input.getDOMNode().value);
|
||||
this.refs.user_id_input.getDOMNode().value = "";
|
||||
this.addUser(this.refs.user_id_input.value);
|
||||
this.refs.user_id_input.value = "";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
|
|
@ -34,7 +34,7 @@ module.exports = React.createClass({
|
|||
render: function(){
|
||||
var VideoView = sdk.getComponent('molecules.voip.VideoView');
|
||||
return (
|
||||
<VideoView ref="video"/>
|
||||
<VideoView ref="video" onClick={ this.props.onClick }/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ module.exports = React.createClass({
|
|||
mixins: [IncomingCallBoxController],
|
||||
|
||||
getRingAudio: function() {
|
||||
return this.refs.ringAudio.getDOMNode();
|
||||
return this.refs.ringAudio;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||
|
@ -29,15 +30,15 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getRemoteVideoElement: function() {
|
||||
return this.refs.remote.getDOMNode();
|
||||
return ReactDOM.findDOMNode(this.refs.remote);
|
||||
},
|
||||
|
||||
getRemoteAudioElement: function() {
|
||||
return this.refs.remoteAudio.getDOMNode();
|
||||
return this.refs.remoteAudio;
|
||||
},
|
||||
|
||||
getLocalVideoElement: function() {
|
||||
return this.refs.local.getDOMNode();
|
||||
return ReactDOM.findDOMNode(this.refs.local);
|
||||
},
|
||||
|
||||
setContainer: function(c) {
|
||||
|
@ -50,7 +51,7 @@ module.exports = React.createClass({
|
|||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
var element = this.container.getDOMNode();
|
||||
var element = this.container;
|
||||
if (payload.fullscreen) {
|
||||
var requestMethod = (
|
||||
element.requestFullScreen ||
|
||||
|
@ -78,7 +79,7 @@ module.exports = React.createClass({
|
|||
render: function() {
|
||||
var VideoFeed = sdk.getComponent('atoms.voip.VideoFeed');
|
||||
return (
|
||||
<div className="mx_VideoView" ref={this.setContainer}>
|
||||
<div className="mx_VideoView" ref={this.setContainer} onClick={ this.props.onClick }>
|
||||
<div className="mx_VideoView_remoteVideoFeed">
|
||||
<VideoFeed ref="remote"/>
|
||||
<audio ref="remoteAudio"/>
|
||||
|
|
|
@ -24,9 +24,6 @@ var sdk = require('matrix-react-sdk')
|
|||
|
||||
var PresetValues = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets').Presets;
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoom',
|
||||
mixins: [CreateRoomController],
|
||||
|
@ -122,6 +119,7 @@ module.exports = React.createClass({
|
|||
render: function() {
|
||||
var curr_phase = this.state.phase;
|
||||
if (curr_phase == this.phases.CREATING) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<Loader/>
|
||||
);
|
||||
|
|
|
@ -17,18 +17,72 @@ 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');
|
||||
|
||||
module.exports = React.createClass({
|
||||
var CallHandler = require("matrix-react-sdk/lib/CallHandler");
|
||||
|
||||
var LeftPanel = React.createClass({
|
||||
displayName: 'LeftPanel',
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showCallElement: null,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
this._recheckCallElement(newProps.selectedRoom);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
// listen for call state changes to prod the render method, which
|
||||
// may hide the global CallView if the call it is tracking is dead
|
||||
case 'call_state':
|
||||
this._recheckCallElement(this.props.selectedRoom);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_recheckCallElement: function(selectedRoomId) {
|
||||
// if we aren't viewing a room with an ongoing call, but there is an
|
||||
// active call, show the call element - we need to do this to make
|
||||
// audio/video not crap out
|
||||
var activeCall = CallHandler.getAnyActiveCall();
|
||||
var callForRoom = CallHandler.getCallForRoom(selectedRoomId);
|
||||
var showCall = (activeCall && !callForRoom);
|
||||
this.setState({
|
||||
showCallElement: showCall
|
||||
});
|
||||
},
|
||||
|
||||
onHideClick: function() {
|
||||
dis.dispatch({
|
||||
action: 'hide_left_panel',
|
||||
});
|
||||
},
|
||||
|
||||
onCallViewClick: function() {
|
||||
var call = CallHandler.getAnyActiveCall();
|
||||
if (call) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: call.roomId,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var RoomList = sdk.getComponent('organisms.RoomList');
|
||||
var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu');
|
||||
|
@ -44,10 +98,17 @@ module.exports = React.createClass({
|
|||
// collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/>
|
||||
}
|
||||
|
||||
var callPreview;
|
||||
if (this.state.showCallElement) {
|
||||
var CallView = sdk.getComponent('molecules.voip.CallView');
|
||||
callPreview = <CallView className="mx_LeftPanel_callView" onClick={this.onCallViewClick} />
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={classes}>
|
||||
{ collapseButton }
|
||||
<IncomingCallBox />
|
||||
{ callPreview }
|
||||
<RoomList selectedRoom={this.props.selectedRoom} collapsed={this.props.collapsed}/>
|
||||
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
||||
</aside>
|
||||
|
@ -55,3 +116,4 @@ module.exports = React.createClass({
|
|||
}
|
||||
});
|
||||
|
||||
module.exports = DragDropContext(HTML5Backend)(LeftPanel);
|
||||
|
|
|
@ -18,9 +18,9 @@ limitations under the License.
|
|||
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var Loader = require('react-loader');
|
||||
|
||||
var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList')
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
|
@ -71,12 +71,13 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onPopulateInvite: function(e) {
|
||||
this.onInvite(this.refs.invite.getDOMNode().value);
|
||||
this.onInvite(this.refs.invite.value);
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
inviteTile: function() {
|
||||
if (this.state.inviting) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
|
@ -104,7 +105,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
return (
|
||||
<div className="mx_MemberList">
|
||||
<div className="mx_MemberList_border">
|
||||
<GeminiScrollbar autoshow={true} className="mx_MemberList_border">
|
||||
{this.inviteTile()}
|
||||
<div>
|
||||
<div className="mx_MemberList_wrapper">
|
||||
|
@ -112,7 +113,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
{invitedSection}
|
||||
</div>
|
||||
</GeminiScrollbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ var Modal = require('matrix-react-sdk/lib/Modal');
|
|||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomDirectory',
|
||||
|
||||
|
@ -110,9 +108,9 @@ module.exports = React.createClass({
|
|||
|
||||
onKeyUp: function(ev) {
|
||||
this.forceUpdate();
|
||||
this.setState({ roomAlias : this.refs.roomAlias.getDOMNode().value })
|
||||
this.setState({ roomAlias : this.refs.roomAlias.value })
|
||||
if (ev.key == "Enter") {
|
||||
this.joinRoom(this.refs.roomAlias.getDOMNode().value);
|
||||
this.joinRoom(this.refs.roomAlias.value);
|
||||
}
|
||||
if (ev.key == "Down") {
|
||||
|
||||
|
@ -121,6 +119,7 @@ module.exports = React.createClass({
|
|||
|
||||
render: function() {
|
||||
if (this.state.loading) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<Loader />
|
||||
|
@ -136,7 +135,9 @@ module.exports = React.createClass({
|
|||
<input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
|
||||
<div className="mx_RoomDirectory_tableWrapper">
|
||||
<table className="mx_RoomDirectory_table">
|
||||
<tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr>
|
||||
<thead>
|
||||
<tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr>
|
||||
</thead>
|
||||
{ this.getRows(this.state.roomAlias) }
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -20,6 +20,7 @@ var React = require('react');
|
|||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var RoomListController = require('../../../../controllers/organisms/RoomList')
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -33,48 +34,82 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var CallView = sdk.getComponent('molecules.voip.CallView');
|
||||
var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
|
||||
|
||||
var callElement;
|
||||
if (this.state.show_call_element) {
|
||||
callElement = <CallView className="mx_MatrixChat_callView"/>
|
||||
}
|
||||
|
||||
var expandButton = this.props.collapsed ?
|
||||
<img className="mx_RoomList_expandButton" onClick={ this.onShowClick } src="img/menu.png" width="20" alt=">"/> :
|
||||
null;
|
||||
|
||||
var invitesLabel = this.props.collapsed ? null : "Invites";
|
||||
var recentsLabel = this.props.collapsed ? null : "Recent";
|
||||
|
||||
var invites;
|
||||
if (this.state.inviteList.length) {
|
||||
invites = <div>
|
||||
<h2 className="mx_RoomList_invitesLabel">{ invitesLabel }</h2>
|
||||
<div className="mx_RoomList_invites">
|
||||
{this.makeRoomTiles(this.state.inviteList, true)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
var RoomSubList = sdk.getComponent('organisms.RoomSubList');
|
||||
var self = this;
|
||||
|
||||
return (
|
||||
<div className="mx_RoomList" onScroll={this._repositionTooltip}>
|
||||
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._repositionTooltip}>
|
||||
<div className="mx_RoomList">
|
||||
{ expandButton }
|
||||
{ callElement }
|
||||
<h2 className="mx_RoomList_favouritesLabel">Favourites</h2>
|
||||
<RoomDropTarget text="Drop here to favourite"/>
|
||||
|
||||
{ invites }
|
||||
<RoomSubList list={ self.state.lists['m.invite'] }
|
||||
label="Invites"
|
||||
editable={ false }
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<h2 className="mx_RoomList_recentsLabel">{ recentsLabel }</h2>
|
||||
<div className="mx_RoomList_recents">
|
||||
{this.makeRoomTiles(this.state.roomList, false)}
|
||||
</div>
|
||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||
label="Favourites"
|
||||
tagName="m.favourite"
|
||||
verb="favourite"
|
||||
editable={ true }
|
||||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<h2 className="mx_RoomList_archiveLabel">Archive</h2>
|
||||
<RoomDropTarget text="Drop here to archive"/>
|
||||
<RoomSubList list={ self.state.lists['m.recent'] }
|
||||
label="Conversations"
|
||||
editable={ true }
|
||||
verb="restore"
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
{ Object.keys(self.state.lists).map(function(tagName) {
|
||||
if (!tagName.match(/^m\.(invite|favourite|recent|lowpriority|archived)$/)) {
|
||||
return <RoomSubList list={ self.state.lists[tagName] }
|
||||
key={ tagName }
|
||||
label={ tagName }
|
||||
tagName={ tagName }
|
||||
verb={ "tag as " + tagName }
|
||||
editable={ true }
|
||||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
}
|
||||
}) }
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.lowpriority'] }
|
||||
label="Low priority"
|
||||
tagName="m.lowpriority"
|
||||
verb="demote"
|
||||
editable={ true }
|
||||
order="recent"
|
||||
bottommost={ self.state.lists['m.archived'].length === 0 }
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.archived'] }
|
||||
label="Historical"
|
||||
editable={ false }
|
||||
order="recent"
|
||||
bottommost={ true }
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
</div>
|
||||
</GeminiScrollbar>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
290
src/skins/vector/views/organisms/RoomSubList.js
Normal file
290
src/skins/vector/views/organisms/RoomSubList.js
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
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 DropTarget = require('react-dnd').DropTarget;
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
// 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,
|
||||
bottommost: React.PropTypes.bool,
|
||||
selectedRoom: React.PropTypes.string.isRequired,
|
||||
activityMap: React.PropTypes.object.isRequired,
|
||||
collapsed: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
hidden: false,
|
||||
sortedList: [],
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.sortList(this.props.list, this.props.order);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
// order the room list appropriately before we re-render
|
||||
//if (debug) console.log("received new props, list = " + newProps.list);
|
||||
this.sortList(newProps.list, newProps.order);
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
this.setState({ hidden : !this.state.hidden });
|
||||
},
|
||||
|
||||
tsOfNewestEvent: function(room) {
|
||||
if (room.timeline.length) {
|
||||
return room.timeline[room.timeline.length - 1].getTs();
|
||||
}
|
||||
else {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: factor the comparators back out into a generic comparator
|
||||
// so that view_prev_room and view_next_room can do the right thing
|
||||
|
||||
recentsComparator: function(roomA, roomB) {
|
||||
return this.tsOfNewestEvent(roomB) - this.tsOfNewestEvent(roomA);
|
||||
},
|
||||
|
||||
manualComparator: function(roomA, roomB) {
|
||||
if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
|
||||
var a = roomA.tags[this.props.tagName].order;
|
||||
var b = roomB.tags[this.props.tagName].order;
|
||||
return a == b ? this.recentsComparator(roomA, roomB) : ( a > b ? 1 : -1);
|
||||
},
|
||||
|
||||
sortList: function(list, order) {
|
||||
if (list === undefined) list = this.state.sortedList;
|
||||
if (order === undefined) order = this.props.order;
|
||||
var comparator;
|
||||
list = list || [];
|
||||
if (order === "manual") comparator = this.manualComparator;
|
||||
if (order === "recent") comparator = this.recentsComparator;
|
||||
|
||||
//if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
|
||||
this.setState({ sortedList: list.sort(comparator) });
|
||||
},
|
||||
|
||||
moveRoomTile: function(room, atIndex) {
|
||||
if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
|
||||
//console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
|
||||
var found = this.findRoomTile(room);
|
||||
var rooms = this.state.sortedList;
|
||||
if (found.room) {
|
||||
if (debug) console.log("removing at index " + found.index + " and adding at index " + atIndex);
|
||||
rooms.splice(found.index, 1);
|
||||
rooms.splice(atIndex, 0, found.room);
|
||||
}
|
||||
else {
|
||||
if (debug) console.log("Adding at index " + atIndex);
|
||||
rooms.splice(atIndex, 0, room);
|
||||
}
|
||||
this.setState({ sortedList: rooms });
|
||||
// console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
|
||||
},
|
||||
|
||||
// XXX: this isn't invoked via a property method but indirectly via
|
||||
// the roomList property method. Unsure how evil this is.
|
||||
removeRoomTile: function(room) {
|
||||
if (debug) console.log("remove room " + room.roomId);
|
||||
var found = this.findRoomTile(room);
|
||||
var rooms = this.state.sortedList;
|
||||
if (found.room) {
|
||||
rooms.splice(found.index, 1);
|
||||
}
|
||||
else {
|
||||
console.warn("Can't remove room " + room.roomId + " - can't find it");
|
||||
}
|
||||
this.setState({ sortedList: rooms });
|
||||
},
|
||||
|
||||
findRoomTile: function(room) {
|
||||
var index = this.state.sortedList.indexOf(room);
|
||||
if (index >= 0) {
|
||||
// console.log("found: room: " + room.roomId + " with index " + index);
|
||||
}
|
||||
else {
|
||||
if (debug) console.log("didn't find room");
|
||||
room = null;
|
||||
}
|
||||
return ({
|
||||
room: room,
|
||||
index: index,
|
||||
});
|
||||
},
|
||||
|
||||
calcManualOrderTagData: function(room) {
|
||||
var index = this.state.sortedList.indexOf(room);
|
||||
|
||||
// we sort rooms by the lexicographic ordering of the 'order' metadata on their tags.
|
||||
// for convenience, we calculate this for now a floating point number between 0.0 and 1.0.
|
||||
|
||||
var orderA = 0.0; // by default we're next to the beginning of the list
|
||||
if (index > 0) {
|
||||
var prevTag = this.state.sortedList[index - 1].tags[this.props.tagName];
|
||||
if (!prevTag) {
|
||||
console.error("Previous room in sublist is not tagged to be in this list. This should never happen.")
|
||||
}
|
||||
else if (prevTag.order === undefined) {
|
||||
console.error("Previous room in sublist has no ordering metadata. This should never happen.");
|
||||
}
|
||||
else {
|
||||
orderA = prevTag.order;
|
||||
}
|
||||
}
|
||||
|
||||
var orderB = 1.0; // by default we're next to the end of the list too
|
||||
if (index < this.state.sortedList.length - 1) {
|
||||
var nextTag = this.state.sortedList[index + 1].tags[this.props.tagName];
|
||||
if (!nextTag) {
|
||||
console.error("Next room in sublist is not tagged to be in this list. This should never happen.")
|
||||
}
|
||||
else if (nextTag.order === undefined) {
|
||||
console.error("Next room in sublist has no ordering metadata. This should never happen.");
|
||||
}
|
||||
else {
|
||||
orderB = nextTag.order;
|
||||
}
|
||||
}
|
||||
|
||||
var order = (orderA + orderB) / 2.0;
|
||||
if (order === orderA || order === orderB) {
|
||||
console.error("Cannot describe new list position. This should be incredibly unlikely.");
|
||||
// TODO: renumber the list
|
||||
}
|
||||
|
||||
return order;
|
||||
},
|
||||
|
||||
makeRoomTiles: function() {
|
||||
var self = this;
|
||||
var RoomTile = sdk.getComponent("molecules.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={ self.props.activityMap[room.roomId] === 1 }
|
||||
highlight={ self.props.activityMap[room.roomId] === 2 }
|
||||
isInvite={ self.props.label === 'Invites' } />
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var connectDropTarget = this.props.connectDropTarget;
|
||||
var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
|
||||
|
||||
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" +
|
||||
(this.props.bottommost ? " mx_RoomSubList_bottommost" : "");
|
||||
|
||||
if (!this.state.hidden) {
|
||||
subList = <div className={ classes }>
|
||||
{ target }
|
||||
{ this.makeRoomTiles() }
|
||||
</div>;
|
||||
}
|
||||
else {
|
||||
subList = <div className={ classes }>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return connectDropTarget(
|
||||
<div>
|
||||
<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-open.png" : "img/list-close.png" } width="10" height="10"/>
|
||||
</h2>
|
||||
{ subList }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="mx_RoomSubList">
|
||||
</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);
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
@ -25,11 +26,9 @@ var sdk = require('matrix-react-sdk')
|
|||
var classNames = require("classnames");
|
||||
var filesize = require('filesize');
|
||||
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var RoomViewController = require('../../../../controllers/organisms/RoomView')
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomView',
|
||||
mixins: [RoomViewController],
|
||||
|
@ -102,9 +101,9 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
scrollToBottom: function() {
|
||||
if (!this.refs.messageWrapper) return;
|
||||
var messageWrapper = this.refs.messageWrapper.getDOMNode();
|
||||
messageWrapper.scrollTop = messageWrapper.scrollHeight;
|
||||
var scrollNode = this._getScrollNode();
|
||||
if (!scrollNode) return;
|
||||
scrollNode.scrollTop = scrollNode.scrollHeight;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -131,6 +130,7 @@ module.exports = React.createClass({
|
|||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
if (this.state.room.currentState.members[myUserId].membership == 'invite') {
|
||||
if (this.state.joining || this.state.rejecting) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<Loader />
|
||||
|
@ -196,10 +196,48 @@ module.exports = React.createClass({
|
|||
);
|
||||
} else {
|
||||
var typingString = this.getWhoIsTypingString();
|
||||
//typingString = "Testing typing...";
|
||||
var unreadMsgs = this.getUnreadMessagesString();
|
||||
// no conn bar trumps unread count since you can't get unread messages
|
||||
// without a connection! (technically may already have some but meh)
|
||||
// It also trumps the "some not sent" msg since you can't resend without
|
||||
// a connection!
|
||||
if (this.state.syncState === "ERROR") {
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_connectionLostBar">
|
||||
<img src="img/warning2.png" width="30" height="30" alt="/!\"/>
|
||||
<div className="mx_RoomView_connectionLostBar_textArea">
|
||||
<div className="mx_RoomView_connectionLostBar_title">
|
||||
Connectivity to the server has been lost.
|
||||
</div>
|
||||
<div className="mx_RoomView_connectionLostBar_desc">
|
||||
Sent messages will be stored until your connection has returned.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else if (this.state.hasUnsentMessages) {
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_connectionLostBar">
|
||||
<img src="img/warning2.png" width="30" height="30" alt="/!\"/>
|
||||
<div className="mx_RoomView_connectionLostBar_textArea">
|
||||
<div className="mx_RoomView_connectionLostBar_title">
|
||||
Some of your messages have not been sent.
|
||||
</div>
|
||||
<div className="mx_RoomView_connectionLostBar_desc">
|
||||
<a className="mx_RoomView_resend_link"
|
||||
onClick={ this.onResendAllClick }>
|
||||
Resend all now
|
||||
</a> or select individual messages to re-send.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// unread count trumps who is typing since the unread count is only
|
||||
// set when you've scrolled up
|
||||
if (unreadMsgs) {
|
||||
else if (unreadMsgs) {
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
|
||||
<img src="img/newmessages.png" width="24" height="24" alt=""/>
|
||||
|
@ -222,6 +260,7 @@ module.exports = React.createClass({
|
|||
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
|
||||
}
|
||||
else if (this.state.uploadingRoomSettings) {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
aux = <Loader/>;
|
||||
}
|
||||
else if (this.state.searching) {
|
||||
|
@ -260,7 +299,7 @@ module.exports = React.createClass({
|
|||
{ conferenceCallNotification }
|
||||
{ aux }
|
||||
</div>
|
||||
<div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
|
||||
<GeminiScrollbar autoshow={true} ref="messagePanel" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
|
||||
<div className="mx_RoomView_messageListWrapper">
|
||||
{ fileDropTarget }
|
||||
<ol className="mx_RoomView_MessageList" aria-live="polite">
|
||||
|
@ -269,14 +308,14 @@ module.exports = React.createClass({
|
|||
{this.getEventTiles()}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</GeminiScrollbar>
|
||||
<div className="mx_RoomView_statusArea">
|
||||
<div className="mx_RoomView_statusAreaBox">
|
||||
<div className="mx_RoomView_statusAreaBox_line"></div>
|
||||
{statusBar}
|
||||
</div>
|
||||
</div>
|
||||
<MessageComposer room={this.state.room} uploadFile={this.uploadFile} />
|
||||
<MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
|||
|
||||
var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings')
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -68,6 +66,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var Loader = sdk.getComponent("atoms.Spinner");
|
||||
switch (this.state.phase) {
|
||||
case this.Phases.Loading:
|
||||
return <Loader />
|
||||
|
|
|
@ -21,12 +21,14 @@ var sdk = require('matrix-react-sdk')
|
|||
|
||||
var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat')
|
||||
|
||||
// should be atomised
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
|
||||
var ContextualMenu = require("../../../../ContextualMenu");
|
||||
var Login = require("../../../../components/login/Login");
|
||||
var Registration = require("../../../../components/login/Registration");
|
||||
var PostRegistration = require("../../../../components/login/PostRegistration");
|
||||
var config = require("../../../../../config.json");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MatrixChat',
|
||||
|
@ -63,6 +65,14 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onLogoutClick: function(event) {
|
||||
dis.dispatch({
|
||||
action: 'logout'
|
||||
});
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
handleResize: function(e) {
|
||||
var hideLhsThreshold = 1000;
|
||||
var showLhsThreshold = 1000;
|
||||
|
@ -92,19 +102,46 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onRegisterClick: function() {
|
||||
this.showScreen("register");
|
||||
},
|
||||
|
||||
onLoginClick: function() {
|
||||
this.showScreen("login");
|
||||
},
|
||||
|
||||
onRegistered: function(credentials) {
|
||||
this.onLoggedIn(credentials);
|
||||
// do post-registration stuff
|
||||
this.showScreen("post_registration");
|
||||
},
|
||||
|
||||
onFinishPostRegistration: function() {
|
||||
// Don't confuse this with "PageType" which is the middle window to show
|
||||
this.setState({
|
||||
screen: undefined
|
||||
});
|
||||
this.showScreen("settings");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var LeftPanel = sdk.getComponent('organisms.LeftPanel');
|
||||
var RoomView = sdk.getComponent('organisms.RoomView');
|
||||
var RightPanel = sdk.getComponent('organisms.RightPanel');
|
||||
var Login = sdk.getComponent('templates.Login');
|
||||
var UserSettings = sdk.getComponent('organisms.UserSettings');
|
||||
var Register = sdk.getComponent('templates.Register');
|
||||
var CreateRoom = sdk.getComponent('organisms.CreateRoom');
|
||||
var RoomDirectory = sdk.getComponent('organisms.RoomDirectory');
|
||||
var MatrixToolbar = sdk.getComponent('molecules.MatrixToolbar');
|
||||
var Notifier = sdk.getComponent('organisms.Notifier');
|
||||
|
||||
if (this.state.logged_in && this.state.ready) {
|
||||
// needs to be before normal PageTypes as you are logged in technically
|
||||
if (this.state.screen == 'post_registration') {
|
||||
return (
|
||||
<PostRegistration
|
||||
onComplete={this.onFinishPostRegistration} />
|
||||
);
|
||||
}
|
||||
else if (this.state.logged_in && this.state.ready) {
|
||||
var page_element;
|
||||
var right_panel = "";
|
||||
|
||||
|
@ -154,21 +191,33 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
} else if (this.state.logged_in) {
|
||||
var Spinner = sdk.getComponent('atoms.Spinner');
|
||||
return (
|
||||
<Loader />
|
||||
<div className="mx_MatrixChat_splash">
|
||||
<Spinner />
|
||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>Logout</a>
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.screen == 'register') {
|
||||
return (
|
||||
<Register onLoggedIn={this.onLoggedIn} clientSecret={this.state.register_client_secret}
|
||||
sessionId={this.state.register_session_id} idSid={this.state.register_id_sid}
|
||||
hsUrl={this.state.register_hs_url} isUrl={this.state.register_is_url}
|
||||
<Registration
|
||||
clientSecret={this.state.register_client_secret}
|
||||
sessionId={this.state.register_session_id}
|
||||
idSid={this.state.register_id_sid}
|
||||
hsUrl={config.default_hs_url}
|
||||
isUrl={config.default_is_url}
|
||||
registrationUrl={this.props.registrationUrl}
|
||||
/>
|
||||
onLoggedIn={this.onRegistered}
|
||||
onLoginClick={this.onLoginClick} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Login onLoggedIn={this.onLoggedIn} />
|
||||
<Login
|
||||
onLoggedIn={this.onLoggedIn}
|
||||
onRegisterClick={this.onRegisterClick}
|
||||
homeserverUrl={config.default_hs_url}
|
||||
identityServerUrl={config.default_is_url} />
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,194 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login')
|
||||
|
||||
var config = require('../../../../../config.json');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Login',
|
||||
mixins: [LoginController],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
serverConfigVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.onHSChosen();
|
||||
this.customHsUrl = config.default_hs_url;
|
||||
this.customIsUrl = config.default_is_url;
|
||||
},
|
||||
|
||||
getHsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customHsUrl;
|
||||
} else {
|
||||
return config.default_hs_url;
|
||||
}
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customIsUrl;
|
||||
} else {
|
||||
return config.default_is_url;
|
||||
}
|
||||
},
|
||||
|
||||
onServerConfigVisibleChange: function(ev) {
|
||||
this.setState({
|
||||
serverConfigVisible: ev.target.checked
|
||||
}, this.onHsUrlChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the form field values for the current login stage
|
||||
*/
|
||||
getFormVals: function() {
|
||||
return {
|
||||
'username': this.refs.user.getDOMNode().value.trim(),
|
||||
'password': this.refs.pass.getDOMNode().value.trim()
|
||||
};
|
||||
},
|
||||
|
||||
onHsUrlChanged: function() {
|
||||
var newHsUrl = this.refs.serverConfig.getHsUrl().trim();
|
||||
var newIsUrl = this.refs.serverConfig.getIsUrl().trim();
|
||||
|
||||
if (newHsUrl == this.customHsUrl &&
|
||||
newIsUrl == this.customIsUrl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.customHsUrl = newHsUrl;
|
||||
this.customIsUrl = newIsUrl;
|
||||
}
|
||||
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this.getHsUrl(),
|
||||
this.getIsUrl()
|
||||
);
|
||||
this.setState({
|
||||
hs_url: this.getHsUrl(),
|
||||
is_url: this.getIsUrl()
|
||||
});
|
||||
// XXX: HSes do not have to offer password auth, so we
|
||||
// need to update and maybe show a different component
|
||||
// when a new HS is entered.
|
||||
if (this.updateHsTimeout) {
|
||||
clearTimeout(this.updateHsTimeout);
|
||||
}
|
||||
var self = this;
|
||||
this.updateHsTimeout = setTimeout(function() {
|
||||
self.onHSChosen();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'choose_hs':
|
||||
case 'fetch_stages':
|
||||
var serverConfigStyle = {};
|
||||
serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
|
||||
var ServerConfig = sdk.getComponent("molecules.ServerConfig");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input className="mx_Login_checkbox" id="advanced" type="checkbox" checked={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
|
||||
<label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label>
|
||||
<div style={serverConfigStyle}>
|
||||
<ServerConfig ref="serverConfig"
|
||||
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
|
||||
onHsUrlChanged={this.onHsUrlChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// XXX: clearly these should be separate organisms
|
||||
case 'stage_m.login.password':
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onUserPassEntered}>
|
||||
<input className="mx_Login_field" ref="user" type="text" value={this.state.username} onChange={this.onUsernameChanged} placeholder="Email or user name" /><br />
|
||||
<input className="mx_Login_field" ref="pass" type="password" value={this.state.password} onChange={this.onPasswordChanged} placeholder="Password" /><br />
|
||||
{ this.componentForStep('choose_hs') }
|
||||
<input className="mx_Login_submit" type="submit" value="Log in" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
case 'stage_m.login.cas':
|
||||
var CasLogin = sdk.getComponent('organisms.CasLogin');
|
||||
return (
|
||||
<CasLogin />
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
onUsernameChanged: function(ev) {
|
||||
this.setState({username: ev.target.value});
|
||||
},
|
||||
|
||||
onPasswordChanged: function(ev) {
|
||||
this.setState({password: ev.target.value});
|
||||
},
|
||||
|
||||
loginContent: function() {
|
||||
var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
|
||||
return (
|
||||
<div>
|
||||
<h2>Sign in</h2>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="mx_Login_error">
|
||||
{ loader }
|
||||
{this.state.errorText}
|
||||
</div>
|
||||
<a className="mx_Login_create" onClick={this.showRegister} href="#">Create a new account</a>
|
||||
<br/>
|
||||
<div className="mx_Login_links">
|
||||
<a href="https://medium.com/@Vector">blog</a> ·
|
||||
<a href="https://twitter.com/@VectorCo">twitter</a> ·
|
||||
<a href="https://github.com/vector-im/vector-web">github</a> ·
|
||||
<a href="https://matrix.org">powered by Matrix</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login">
|
||||
<div className="mx_Login_box">
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="249" height="78" alt="vector"/>
|
||||
</div>
|
||||
{this.loginContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -1,201 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var RegisterController = require('../../../../controllers/templates/Register')
|
||||
|
||||
var config = require('../../../../../config.json');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Register',
|
||||
mixins: [RegisterController],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
serverConfigVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.customHsUrl = config.default_hs_url;
|
||||
this.customIsUrl = config.default_is_url;
|
||||
},
|
||||
|
||||
getRegFormVals: function() {
|
||||
return {
|
||||
email: this.refs.email.getDOMNode().value.trim(),
|
||||
username: this.refs.username.getDOMNode().value.trim(),
|
||||
password: this.refs.password.getDOMNode().value.trim(),
|
||||
confirmPassword: this.refs.confirmPassword.getDOMNode().value.trim()
|
||||
};
|
||||
},
|
||||
|
||||
getHsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customHsUrl;
|
||||
} else {
|
||||
return config.default_hs_url;
|
||||
}
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customIsUrl;
|
||||
} else {
|
||||
return config.default_is_url;
|
||||
}
|
||||
},
|
||||
|
||||
onServerConfigVisibleChange: function(ev) {
|
||||
this.setState({
|
||||
serverConfigVisible: ev.target.checked
|
||||
});
|
||||
},
|
||||
|
||||
onServerUrlChanged: function(newUrl) {
|
||||
this.customHsUrl = this.refs.serverConfig.getHsUrl();
|
||||
this.customIsUrl = this.refs.serverConfig.getIsUrl();
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
onProfileContinueClicked: function() {
|
||||
this.onAccountReady();
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'initial':
|
||||
var serverConfigStyle = {};
|
||||
serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
|
||||
var ServerConfig = sdk.getComponent("molecules.ServerConfig");
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onInitialStageSubmit}>
|
||||
<input className="mx_Login_field" type="text" ref="email" placeholder="Email address" defaultValue={this.savedParams.email} /><br />
|
||||
<input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} /><br />
|
||||
<input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br />
|
||||
<input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br />
|
||||
|
||||
<input className="mx_Login_checkbox" id="advanced" type="checkbox" value={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
|
||||
<label htmlFor="advanced">Use custom server options (advanced)</label>
|
||||
<div style={serverConfigStyle}>
|
||||
<ServerConfig ref="serverConfig"
|
||||
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
|
||||
onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} />
|
||||
</div>
|
||||
<br />
|
||||
<input className="mx_Login_submit" type="submit" value="Register" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
// XXX: clearly these should be separate organisms
|
||||
case 'stage_m.login.email.identity':
|
||||
return (
|
||||
<div>
|
||||
Please check your email to continue registration.
|
||||
</div>
|
||||
);
|
||||
case 'stage_m.login.recaptcha':
|
||||
return (
|
||||
<div ref="recaptchaContainer">
|
||||
This Home Server would like to make sure you are not a robot
|
||||
<div id="mx_recaptcha"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
registerContent: function() {
|
||||
if (this.state.busy) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else if (this.state.step == 'profile') {
|
||||
var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName');
|
||||
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
|
||||
return (
|
||||
<div className="mx_Login_profile">
|
||||
Set a display name:
|
||||
<ChangeDisplayName />
|
||||
Upload an avatar:
|
||||
<ChangeAvatar initialAvatarUrl={MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl)} />
|
||||
<button onClick={this.onProfileContinueClicked}>Continue</button>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h2>Create an account</h2>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="mx_Login_error">{this.state.errorText}</div>
|
||||
<a className="mx_Login_create" onClick={this.showLogin} href="#">I already have an account</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
onBadFields: function(bad) {
|
||||
var keys = Object.keys(bad);
|
||||
var strings = [];
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
switch (bad[keys[i]]) {
|
||||
case this.FieldErrors.PasswordMismatch:
|
||||
strings.push("Passwords don't match");
|
||||
break;
|
||||
case this.FieldErrors.Missing:
|
||||
strings.push("Missing "+keys[i]);
|
||||
break;
|
||||
case this.FieldErrors.TooShort:
|
||||
strings.push(keys[i]+" is too short");
|
||||
break;
|
||||
case this.FieldErrors.InUse:
|
||||
strings.push(keys[i]+" is already taken");
|
||||
break;
|
||||
case this.FieldErrors.Length:
|
||||
strings.push(keys[i] + " is not long enough.");
|
||||
break;
|
||||
default:
|
||||
console.error("Unhandled FieldError: %s", bad[keys[i]]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var errtxt = strings.join(', ');
|
||||
this.setState({
|
||||
errorText: errtxt
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login">
|
||||
<div className="mx_Login_box">
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="249" height="78" alt="vector"/>
|
||||
</div>
|
||||
{this.registerContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue