mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 13:14:58 +08:00
Merge branch 'experimental' into travis/rsettings/tab/general
This commit is contained in:
commit
ad276c1fda
@ -33,6 +33,7 @@
|
||||
@import "./views/auth/_InteractiveAuthEntryComponents.scss";
|
||||
@import "./views/auth/_LanguageSelector.scss";
|
||||
@import "./views/auth/_ServerConfig.scss";
|
||||
@import "./views/auth/_ServerTypeSelector.scss";
|
||||
@import "./views/avatars/_BaseAvatar.scss";
|
||||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||
|
@ -64,3 +64,20 @@ body.mx_scrollbar_nooverlay {
|
||||
margin-right: calc(-1 * var(--scrollbar-width));
|
||||
}
|
||||
}
|
||||
|
||||
// style the native scrollbars ...
|
||||
// ... standard css scrollbars (firefox at time of writing)
|
||||
.mx_AutoHideScrollbar {
|
||||
scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
// or fallback for webkit browsers
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
background-color: $scrollbar-track-color;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: $scrollbar-thumb-color;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ limitations under the License.
|
||||
.mx_TabbedView_maskedIcon {;
|
||||
margin-left: 6px;
|
||||
margin-right: 9px;
|
||||
margin-top: 1px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: inline-block;
|
||||
@ -66,7 +67,7 @@ limitations under the License.
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: 14px;
|
||||
width: 14px;
|
||||
height: 15px;
|
||||
height: 18px;
|
||||
mask-position: center;
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
|
@ -23,7 +23,8 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_Login_field {
|
||||
width: 280px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $strong-input-border-color;
|
||||
font-weight: 300;
|
||||
@ -32,10 +33,6 @@ limitations under the License.
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.mx_Login_field_disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.mx_Login_fieldLabel {
|
||||
margin-top: -10px;
|
||||
margin-left: 8px;
|
||||
@ -49,6 +46,8 @@ limitations under the License.
|
||||
width: 100%;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 24px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_Login_submit:hover {
|
||||
@ -69,10 +68,10 @@ limitations under the License.
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_Login_sso_link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
.mx_AuthBody a.mx_Login_sso_link:link,
|
||||
.mx_AuthBody a.mx_Login_sso_link:hover,
|
||||
.mx_AuthBody a.mx_Login_sso_link:visited {
|
||||
color: $button-primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_Login_loader {
|
||||
@ -105,6 +104,7 @@ limitations under the License.
|
||||
.mx_Login_type_container {
|
||||
display: flex;
|
||||
margin-bottom: 14px;
|
||||
color: $primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_Login_type_label {
|
||||
@ -124,9 +124,9 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_Login_field_prefix {
|
||||
height: 34px;
|
||||
height: 38px;
|
||||
padding: 0px 5px;
|
||||
line-height: 33px;
|
||||
line-height: 38px;
|
||||
|
||||
background-color: #eee;
|
||||
border: 1px solid #c7c7c7;
|
||||
@ -136,40 +136,11 @@ limitations under the License.
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_Login_field_suffix {
|
||||
height: 34px;
|
||||
padding: 0px 5px;
|
||||
line-height: 33px;
|
||||
|
||||
background-color: #eee;
|
||||
border: 1px solid #c7c7c7;
|
||||
border-left: 0px;
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.mx_Login_username {
|
||||
height: 16px;
|
||||
flex-shrink: 1;
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneNumberField {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.mx_Login_field_has_prefix {
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_field_has_suffix {
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneSection {
|
||||
display:flex;
|
||||
}
|
||||
@ -190,12 +161,9 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_Login_phoneCountry .mx_Dropdown_option {
|
||||
/*
|
||||
To match height of mx_Login_field
|
||||
33px + 2px border from mx_Dropdown_option = 35px
|
||||
*/
|
||||
height: 33px;
|
||||
line-height: 33px;
|
||||
/* To match height of mx_Login_field */
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneCountry .mx_Dropdown_option img {
|
||||
|
@ -24,6 +24,29 @@ limitations under the License.
|
||||
color: $authpage-body-color;
|
||||
}
|
||||
|
||||
.mx_AuthBody h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.mx_AuthBody h3 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_Auth_editServerDetails {
|
||||
padding-left: 1em;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mx_AuthBody .mx_Field input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mx_AuthBody a:link,
|
||||
.mx_AuthBody a:hover,
|
||||
.mx_AuthBody a:visited {
|
||||
|
@ -23,12 +23,6 @@ limitations under the License.
|
||||
background-color: $authpage-bg-color;
|
||||
}
|
||||
|
||||
.mx_AuthPage h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.mx_AuthPage_modal {
|
||||
display: flex;
|
||||
margin: 100px auto auto;
|
||||
|
@ -14,23 +14,24 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_ServerConfig {
|
||||
margin-top: 7px;
|
||||
.mx_ServerConfig_fields {
|
||||
display: flex;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.mx_ServerConfig .mx_Login_field {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 5px;
|
||||
.mx_ServerConfig_fields .mx_Field {
|
||||
flex: 1;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.mx_ServerConfig_fields .mx_Field:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mx_ServerConfig_fields .mx_Field:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.mx_ServerConfig_help:link {
|
||||
opacity: 0.8;
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
color: $primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_ServerConfig_selector {
|
||||
text-align: center;
|
||||
width: 302px; // for fr i18n
|
||||
}
|
69
res/css/views/auth/_ServerTypeSelector.scss
Normal file
69
res/css/views/auth/_ServerTypeSelector.scss
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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_ServerTypeSelector {
|
||||
display: flex;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_type {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_type:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_type:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_label {
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: $primary-fg-color;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_type .mx_AccessibleButton {
|
||||
padding: 10px;
|
||||
border: 1px solid $input-border-color;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_type.mx_ServerTypeSelector_type_selected .mx_AccessibleButton {
|
||||
border-color: $input-valid-border-color;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 18px;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 600;
|
||||
color: $primary-fg-color;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_logo > div {
|
||||
display: flex;
|
||||
width: 70%;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.mx_ServerTypeSelector_description {
|
||||
font-size: 10px;
|
||||
}
|
@ -44,3 +44,7 @@ limitations under the License.
|
||||
.mx_UserSettingsDialog_labsIcon:before {
|
||||
mask-image: url('$(res)/img/feather-icons/flag.svg');
|
||||
}
|
||||
|
||||
.mx_UserSettingsDialog_flairIcon:before {
|
||||
mask-image: url('$(res)/img/feather-icons/flair.svg');
|
||||
}
|
||||
|
@ -20,23 +20,14 @@ limitations under the License.
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.mx_GroupPublicity_toggle > label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.mx_GroupPublicity_toggle > label,
|
||||
.mx_GroupPublicity_toggle .mx_GroupTile {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.mx_GroupPublicity_toggle input {
|
||||
margin-right: 8px;
|
||||
vertical-align: -4px;
|
||||
}
|
||||
|
||||
.mx_GroupPublicity_toggle .mx_GroupTile {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_GroupPublicity_toggle .mx_ToggleSwitch {
|
||||
float: right;
|
||||
}
|
||||
|
6
res/img/feather-icons/flair.svg
Normal file
6
res/img/feather-icons/flair.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="14" viewBox="0 0 10 14">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#454545" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
||||
<ellipse cx="4" cy="3.818" rx="4" ry="3.818"/>
|
||||
<path d="M1.834 7.03L1.143 12 4 10.364 6.857 12l-.691-4.975"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 357 B |
7
res/img/feather-icons/globe.svg
Normal file
7
res/img/feather-icons/globe.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg">
|
||||
<g style="stroke:#454545;stroke-width:.8;fill:none;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round" transform="translate(1 1)">
|
||||
<circle cx="5" cy="5" r="5"/>
|
||||
<path d="m0 5h10"/>
|
||||
<path d="m5 0c1.25064019 1.36917645 1.96137638 3.14601693 2 5-.03862362 1.85398307-.74935981 3.63082355-2 5-1.25064019-1.36917645-1.96137638-3.14601693-2-5 .03862362-1.85398307.74935981-3.63082355 2-5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 524 B |
13
res/img/feather-icons/matrix-org-bw-logo.svg
Normal file
13
res/img/feather-icons/matrix-org-bw-logo.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg height="18" viewBox="0 0 66 18" width="66" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#454545" fill-rule="evenodd">
|
||||
<path d="m.57375773.51260411v16.96465689h1.22032333v.4070323h-1.68861516v-17.77866867h1.68861516v.40697948z"/>
|
||||
<path d="m5.27530002 5.89064516v.85851027h.02442757c.22886998-.32679766.50453749-.58053959.82747736-.76043402.32272883-.17957771.69267726-.26973607 1.10937046-.26973607.4003378 0 .76611825.07785923 1.09702477.23304985.33101205.15545455.5823575.42920235.75398361.82171848.18782322-.27791789.44317838-.52326687.76611825-.73583578.32272886-.21246334.70475916-.31893255 1.14614376-.31893255.3350745 0 .6454576.04096187.9317297.12272727.2858501.0817654.5310227.21256891.7353596.39241056.2041259.18.363459.41505572.4781051.70506158.1143294.29053373.1716261.63987097.1716261 1.04880352v4.24324923l-1.7407414-.0000527v-3.59334902c0-.21246335-.0081249-.41278593-.0245331-.60096774-.0165137-.18791789-.0612536-.35134311-.1348001-.49053959-.0735993-.13887977-.1820197-.24936071-.3248392-.33117889-.1430305-.08144868-.3371849-.12256891-.5823047-.12256891-.2451199 0-.4433367.04729618-.59454494.1409912-.15131376.09417009-.2697057.21689736-.35543961.36797067-.08578668.15144282-.14303055.32304986-.17157335.51529619-.02875383.19203519-.04294609.38628739-.04294609.58249267v3.53190612h-1.7405831v-3.55645158c0-.18802346-.00427351-.37409384-.01213465-.55794721-.00833598-.18406452-.0429461-.35366569-.10435796-.50912023-.06130634-.15519062-.16350117-.27997654-.30647895-.37414663-.14303055-.09369502-.35343476-.1409912-.6312654-.1409912-.08182973 0-.19009187.01842228-.32478645.05531964-.13485285.03679179-.26569599.10641643-.39226563.20845162-.12683343.10229912-.23499006.24946627-.32489196.44144868-.08995466.19224633-.13485285.44350733-.13485285.75415249v3.67923172l-1.74053034.0000527v-6.34059234z"/>
|
||||
<path d="m14.4319985 6.82284457c.1796456-.26989442.4085683-.48642228.6865045-.65011143.2777251-.16347801.5901658-.27992376.937586-.3494956.347262-.06941349.696687-.10430499 1.0480641-.10430499.3187719 0 .641448.02259238.9683448.06740763.3268441.04513196.6251981.1331261.8949038.26377126.2696529.13075073.4902397.31280938.6619186.54570088.1716261.23299706.2574127.54168915.2574127.92602346v3.29880352c0 .286522.0163554.5601114.049119.8217185.032447.2617654.0897436.4579706.1715206.5887214h-1.7650635c-.0327635-.0981818-.059407-.1982112-.0797193-.3005103-.0205762-.1020352-.0348739-.2062874-.0428933-.312651-.2778834.2862053-.6048858.4865278-.9805322.6010205-.3760158.114176-.7601037.1716071-1.1523693.1716071-.3024693 0-.5844152-.0368446-.8458904-.1103226-.2615808-.0736364-.4902925-.1880235-.6863989-.3436364-.1961592-.155085-.3493723-.3513431-.4596921-.5884047-.1103197-.2370616-.1655587-.5194135-.1655587-.8463167 0-.3596305.0632584-.65612903.1901446-.88891495.1265696-.23315543.2899125-.41906745.490398-.55821115.2001161-.13893255.4288806-.24318475.6863989-.31265102.25736-.06941349.5167776-.12446921.7783056-.16558945.261528-.04075073.5188881-.07353079.7722911-.09802346.2532976-.02459824.4781051-.06144281.6742115-.11048094.1961592-.04903812.3512717-.12045747.4658122-.21457478.1142239-.09401173.1673526-.23093841.1593331-.41093841 0-.18791789-.0306004-.33724927-.0919067-.44757185-.0612536-.11032258-.1430306-.19631085-.2451199-.25759531-.1021948-.06128445-.2206922-.10219355-.3554396-.12272727-.1349056-.02016422-.2800993-.03061584-.4351589-.03061584-.3432523 0-.6129052.07363637-.8090644.22080352-.1960537.14716716-.3106997.39256892-.3432522.73583578h-1.7405831c.0243748-.40866862.1263586-.7481349.3063734-1.0177654zm3.4382729 2.45892669c-.110267.036739-.2288172.06730205-.3553869.09190029-.1267279.02454546-.2594703.0450264-.3983857.06128446-.1390208.016522-.2779361.03684458-.4167459.06139003-.1308432.02433431-.2595232.05732551-.3860401.09802346-.1268334.04112024-.2371531.09617595-.331012.16564223-.0941226.06951906-.169674.15756598-.2268651.26361287-.0572439.1063109-.0857867.2412845-.0857867.4047625 0 .1551906.0285428.2862053.0857867.3922522.0571911.1064692.1349583.1902405.2329852.2515249.0980268.0612845.2123563.104305.343305.1286921.1305793.0245982.2654321.0368446.4044002.0368446.3431995 0 .6086844-.0571144.7968242-.1717126.187876-.1143343.3266858-.2513666.416746-.4109384.0897963-.1593608.1450354-.3205162.165506-.4842581.0202596-.163478.0306004-.29433432.0306004-.39241057v-.65011144c-.073652.06561291-.1655588.1166569-.2759313.15350147z" fill-rule="nonzero"/>
|
||||
<path d="m24.4710969 5.89064516v1.16509091h-1.2747182v3.13965393c0 .294176.0489606.4904868.147093.5885103.0979213.098129.294186.1471671.588372.1471671.0980796 0 .1919385-.0040117.2818931-.0122463.0898492-.0080234.1756886-.0204809.2573601-.036739v1.3490499c-.1470931.0245454-.3105942.0408035-.4902925.0491437-.179751.0078651-.3554396.0121407-.5270657.0121407-.269653 0-.5251136-.0184751-.7661183-.0553196-.2411629-.0366862-.4535719-.1080528-.6373855-.2144692-.183919-.1061525-.3290071-.2575953-.4351589-.4538006-.1063628-.1960469-.159386-.453695-.159386-.7727332v-3.74035773h-1.0541314v-1.16509091h1.0541314v-1.90092668h1.7406359v1.90092668z"/>
|
||||
<path d="m26.9593852 5.89064516v1.17739003h.0245331c.0815659-.19636364.1918329-.37810557.3309065-.54585924.1389681-.16743695.2981958-.31064516.4779996-.42920234.1796455-.11834604.371795-.21035191.5762901-.27596481.2040731-.06529619.4166932-.09812903.6373855-.09812903.1143823 0 .2408991.02058651.3799727.06139002v1.61894429c-.0818297-.01646921-.1799093-.03082698-.294186-.04296775-.1144878-.01229912-.2247548-.01847507-.331012-.01847507-.3187192 0-.5883721.05331378-.8089061.15946628-.2206395.10631085-.398333.25131378-.5332386.43532551-.1348001.18406452-.2310331.39853372-.2881714.6438827-.0571384.24519061-.085734.51107331-.085734.79717302v2.85746043h-1.7405831v-6.34043404z"/>
|
||||
<path d="m30.121885 4.90946041v-1.43487976h1.7407414v1.43487976zm1.7407414.98118475v6.34048684h-1.7407414v-6.34048684z" fill-rule="nonzero"/>
|
||||
<path d="m32.7696121 5.89064516h1.9857029l1.1155433 1.6556305 1.1032504-1.6556305h1.9244494l-2.0837826 2.96794135 2.3413009 3.37259829h-1.9858612l-1.3238899-1.999003-1.3239427 1.999003h-1.9488242l2.279889-3.33570093z"/>
|
||||
<path d="m65.2811695 17.477261v-16.96465689h-1.2203233v-.40697948h1.6887206v17.77866867h-1.6887206v-.4070323z"/>
|
||||
<path d="m40.9083636 10.4624727h1.9011273v1.8648h-1.9011273zm4.8194182-1.25934543c0 .2502558.024218.49445336.0726546.7326.0484366.23814663.1311812.45207183.2482363.64178183.1170552.18971.2724536.3410722.4662.4540909.1937465.1130187.4359258.1695273.7265455.1695273.2906196 0 .5348172-.0565086.7326-.1695273s.3551994-.2643809.4722545-.4540909c.1170552-.18971.1997998-.4036352.2482364-.64178183.0484366-.23814664.0726545-.4823442.0726545-.7326 0-.25025579-.0242179-.49647151-.0726545-.73865454s-.1311812-.45610817-.2482364-.64178182c-.1170551-.18567366-.2744717-.33703578-.4722545-.45409091s-.4419804-.17558182-.7326-.17558182c-.2906197 0-.532799.05852669-.7265455.17558182-.1937464.11705513-.3491448.26841725-.4662.45409091-.1170551.18567365-.1997997.39959879-.2482363.64178182s-.0726546.48839875-.0726546.73865454zm-1.7194909 0c0-.50051159.0766902-.95459796.2300727-1.36227272.1533826-.40767477.3713441-.75479857.6538909-1.04138182.2825469-.28658325.6215981-.50858103 1.0171637-.666s.8395612-.23612728 1.332-.23612728.9384525.07870831 1.3380545.23612728.7406713.37941675 1.0232182.666.5005083.63370705.6538909 1.04138182c.1533826.40767476.2300727.86176113.2300727 1.36227272 0 .5005116-.0766901.95257983-.2300727 1.35621823-.1533826.4036383-.371344.748744-.6538909 1.0353272-.2825469.2865833-.6236162.5065629-1.0232182.6599455s-.8456157.2300727-1.3380545.2300727-.9364344-.0766901-1.332-.2300727-.7346168-.3733622-1.0171637-.6599455c-.2825468-.2865832-.5005083-.6316889-.6538909-1.0353272-.1533825-.4036384-.2300727-.85570663-.2300727-1.35621823zm7.5924-3.13625454h1.6347273v1.16247272h.0242182c.0807276-.19374642.1897084-.3733628.3269454-.53885454.1372371-.16549174.2946537-.30676305.4722546-.42381818s.367308-.20787241.5691272-.27245455.411708-.09687273.6296728-.09687273c.1130187 0 .2381447.02018162.3753818.06054546v1.5984c-.0807277-.01614554-.1775995-.03027267-.2906182-.04238182s-.2219995-.01816364-.3269455-.01816364c-.3148379 0-.5812352.05247221-.7992.15741819-.2179647.10494598-.3935447.24823545-.5267454.42987272-.1332007.18163728-.2280543.39354425-.2845636.63572728-.0565094.24218303-.0847637.50454404-.0847637.78709091v2.82141815h-1.7194909zm7.2896727 4.78309087c.2421831 0 .4561082-.0484358.6417819-.1453091.1856736-.0968732.3390539-.2260355.4601454-.3874909.1210915-.1614553.2119088-.34510803.2724546-.5509636.0605457-.20585557.0908181-.42179887.0908181-.64783636 0-.25832857-.0242179-.50252613-.0726545-.7326-.0484366-.23007388-.1291631-.43390821-.2421818-.61150909-.1130188-.17760089-.2623627-.31887221-.4480364-.42381819-.1856736-.10494598-.4197804-.15741818-.7023273-.15741818-.242183 0-.4500536.04843588-.6236181.14530909s-.3188722.22805372-.4359273.39354546c-.1170551.16549173-.2018179.35519893-.2542909.56912727s-.0787091.43794428-.0787091.67205455c0 .22603749.0221998.45005343.0666.67205454s.1210903.42179911.2300727.59940001.2522718.3229085.4298727.4359273c.1776009.1130187.3995987.1695272.666.1695272zm3.0999273 1.0777091c0 .2502558-.0343087.5368348-.1029273.8597455-.0686185.3229107-.2159443.6236168-.4419818.9021273-.2260375.2785104-.5529797.5146353-.9808363.7083818-.4278567.1937464-1.0010146.2906182-1.719491.2906182-.3067651 0-.6195802-.0383451-.9384545-.1150364s-.6094896-.1957628-.8718545-.3572182c-.262365-.1614553-.4803265-.369326-.653891-.6236182s-.2724544-.5590346-.2966727-.9142363h1.7073818c.0807277.3229107.2381443.5469266.4722546.6720545.2341102.1251279.5045439.1876909.8113091.1876909.484366 0 .8375443-.1453076 1.0595454-.4359273.2220011-.2906196.3289637-.657925.3208909-1.1019272v-.8234182h-.0242181c-.1856737.3309835-.4500529.575181-.7931455.7326s-.7043436.2361273-1.0837636.2361273c-.4682206 0-.8718529-.0827447-1.2109091-.2482364-.3390563-.1654917-.6175626-.3915258-.8355273-.6781091-.2179647-.2865832-.3773995-.6215981-.4783091-1.0050545-.1009096-.3834565-.1513636-.78910699-.1513636-1.21696367 0-.40363838.0585267-.79314358.1755818-1.16852728.1170551-.37538369.2885989-.7063622.5146364-.99294545.2260374-.28658325.5065619-.51463552.8415818-.68416364.3350198-.16952812.7204887-.25429091 1.1564182-.25429091.4117111 0 .772962.07669015 1.0837636.23007273.3108015.15338259.5711444.41170728.7810364.77498182h.0242181v-.83552727h1.6347273z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
18
res/img/feather-icons/modular-bw-logo.svg
Normal file
18
res/img/feather-icons/modular-bw-logo.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg height="12" viewBox="0 0 71 12" width="71" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#454545" fill-rule="evenodd">
|
||||
<g transform="translate(18.551433 2)">
|
||||
<path d="m.06753266 7.63333335v-5.66666669h1.65455009v.63333334c.30389695-.5 1.01298984-.83333333 1.65455008-.83333333.81039188 0 1.41818577.33333333 1.68831641.9.43896227-.63333333 1.01298982-.9 1.7896154-.9 1.08052251 0 2.1272787.63333333 2.1272787 2.16666667v3.66666666h-1.68831643v-3.26666666c0-.53333334-.30389694-.96666667-.91169086-.96666667-.60779387 0-.94545718.46666667-.94545718.96666667v3.26666666h-1.72208276v-3.26666666c0-.53333334-.30389696-.96666667-.91169086-.96666667-.60779391 0-.94545719.46666667-.94545719.96666667v3.26666666h-1.7896154z"/>
|
||||
<path d="m16.9844631 4.8c0 1.76666665-1.3168868 3-3.0389695 3-1.6883165 0-3.0389695-1.26666665-3.0389695-3 0-1.76666666 1.350653-3 3.0389695-3 1.7220827 0 3.0389695 1.26666667 3.0389695 3m-1.755849 0c0-.96666666-.6077939-1.4-1.2831205-1.4-.6415602 0-1.2831205.43333334-1.2831205 1.4 0 .93333335.6415603 1.4 1.2831205 1.4.6753266.03333335 1.2831205-.43333335 1.2831205-1.4" fill-rule="nonzero"/>
|
||||
<path d="m30.9974894 7.06666665c-.303897.5-.9454572.7-1.5194848.7-1.3844195 0-2.161045-1-2.161045-2.2v-3.56666665h1.7558491v3.2c0 .53333335.3038969.96666665.9116908.96666665.5740276 0 .9454572-.4.9454572-.96666665v-3.2h1.7558491v4.63333335c0 .5.0337663.9333333.0675327 1h-1.6883165c-.0337663-.1-.0675326-.4-.0675326-.5666667"/>
|
||||
<path d="m43.2209002 6.9c-.2701306.53333335-.8441582.9-1.553251.9-1.6883165 0-2.8363717-1.26666665-2.8363717-3 0-1.66666666 1.0805225-2.96666666 2.8026053-2.96666666 1.0129899 0 1.4857184.56666666 1.5870174.83333333v-.66666667h1.6883166v4.63333335c0 .5333333.0337662.9.0337662 1h-1.6883165c-.0337663-.13333335-.0337663-.43333335-.0337663-.6666667zm-1.3168868-.6c.7090929 0 1.3168868-.53333335 1.3168868-1.5 0-.96666667-.5740275-1.5-1.3168868-1.5-.7428592 0-1.3168868.5-1.3168868 1.5 0 .93333335.6077939 1.5 1.3168868 1.5z" fill-rule="nonzero"/>
|
||||
<path d="m50.8520906 1.9c.6415599 0 1.1480552.5 1.1480552 1.13333334 0 .63333333-.5064953 1.1-1.1480552 1.1-.6415602 0-1.1142889-.5-1.1142889-1.1 0-.66666667.4727287-1.13333334 1.1142889-1.13333334zm-1.7558494 5.73333335h-1.7558491v-5.66666669h1.7558491z" fill-rule="nonzero"/>
|
||||
<path d="m35.083215.266667h1.755849v7.4h-1.755849z"/>
|
||||
<path d="m24.9195503.26666667h-1.7220828v2.16666666c-.1350653-.2-.5740276-.56666667-1.5532511-.56666667-1.6207837 0-2.7688389 1.26666667-2.7688389 2.93333334 0 1.73333335 1.2155878 2.96666665 2.8363716 2.96666665.6753266 0 1.2831204-.3 1.5194848-.66666665 0 .23333335.0337663.46666665.0337663.53333335h1.6883164c0-.13333335-.0337663-.5-.0337663-1zm-3.0052032 5.96666668c-.6753266 0-1.2831205-.4666667-1.2831205-1.40000001 0-.93333334.6077939-1.4 1.2831205-1.4.6753265 0 1.2831204.46666666 1.2831204 1.4 0 .90000001-.6077939 1.40000001-1.2831204 1.40000001z" fill-rule="nonzero"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m14.0349329 1.05652174-1.7385268-.97826087-1.7385268.97826087-.0132712 9.88695656 1.751798.9913043 1.7385268-.9782608z"/>
|
||||
<path d="m3.59050092 6-1.751798-.9782609-1.73852679.9782609v4.9434783l1.75179799.9913043 1.7385268-.9913043z"/>
|
||||
<path d="m7.0675545 6.99130435 2.60115457-1.47391305v-1.98260869l-1.73852676-.97826087-.86262781.50869565-3.49032478-1.99565217-1.7385268-.99130435-1.73852679.99130435v1.96956521z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
@ -74,7 +74,9 @@ $strong-input-border-color: #656565;
|
||||
// used for UserSettings EditableText
|
||||
$input-underline-color: $primary-fg-color;
|
||||
$input-fg-color: $primary-fg-color;
|
||||
|
||||
// scrollbars
|
||||
$scrollbar-thumb-color: rgba(255, 255, 255, 0.2);
|
||||
$scrollbar-track-color: transparent;
|
||||
// context menus
|
||||
$menu-border-color: rgba(187, 187, 187, 0.5);
|
||||
$menu-bg-color: #373737;
|
||||
|
@ -86,7 +86,9 @@ $strong-input-border-color: #c7c7c7;
|
||||
// used for UserSettings EditableText
|
||||
$input-underline-color: rgba(151, 151, 151, 0.5);
|
||||
$input-fg-color: rgba(74, 74, 74, 0.9);
|
||||
|
||||
// scrollbars
|
||||
$scrollbar-thumb-color: rgba(0, 0, 0, 0.2);
|
||||
$scrollbar-track-color: transparent;
|
||||
// context menus
|
||||
$menu-border-color: #ebedf8;
|
||||
$menu-bg-color: #fff;
|
||||
|
@ -83,7 +83,9 @@ $field-focused-label-bg-color: #ffffff;
|
||||
// used for UserSettings EditableText
|
||||
$input-underline-color: rgba(151, 151, 151, 0.5);
|
||||
$input-fg-color: rgba(74, 74, 74, 0.9);
|
||||
|
||||
// scrollbars
|
||||
$scrollbar-thumb-color: rgba(0, 0, 0, 0.2);
|
||||
$scrollbar-track-color: transparent;
|
||||
// context menus
|
||||
$menu-border-color: rgba(187, 187, 187, 0.5);
|
||||
$menu-bg-color: #f6f6f6;
|
||||
|
@ -66,7 +66,7 @@ const customVariables = {
|
||||
},
|
||||
'User Type': {
|
||||
id: 3,
|
||||
expl: _td('Whether or not you\'re logged in (we don\'t record your user name)'),
|
||||
expl: _td('Whether or not you\'re logged in (we don\'t record your username)'),
|
||||
example: 'Logged In',
|
||||
},
|
||||
'Chosen Language': {
|
||||
|
@ -20,6 +20,7 @@ import React from "react";
|
||||
// Copyright (c) Noel Delgado <pixelia.me@gmail.com> (pixelia.me)
|
||||
function getScrollbarWidth(alternativeOverflow) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'mx_AutoHideScrollbar'; //to get width of css scrollbar
|
||||
div.style.position = 'absolute';
|
||||
div.style.top = '-9999px';
|
||||
div.style.width = '100px';
|
||||
|
@ -84,7 +84,7 @@ const RoomSubList = React.createClass({
|
||||
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
||||
isCollapsableOnClick: function() {
|
||||
const stuck = this.refs.header.dataset.stuck;
|
||||
if (this.state.hidden || stuck === undefined || stuck === "none") {
|
||||
if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -238,7 +238,7 @@ const RoomSubList = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_getHeaderJsx: function() {
|
||||
_getHeaderJsx: function(isCollapsed) {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const subListNotifications = this.roomNotificationCount();
|
||||
const subListNotifCount = subListNotifications[0];
|
||||
@ -287,8 +287,8 @@ const RoomSubList = React.createClass({
|
||||
if (len) {
|
||||
const chevronClasses = classNames({
|
||||
'mx_RoomSubList_chevron': true,
|
||||
'mx_RoomSubList_chevronRight': this.state.hidden,
|
||||
'mx_RoomSubList_chevronDown': !this.state.hidden,
|
||||
'mx_RoomSubList_chevronRight': isCollapsed,
|
||||
'mx_RoomSubList_chevronDown': !isCollapsed,
|
||||
});
|
||||
chevron = (<div className={chevronClasses}></div>);
|
||||
}
|
||||
@ -321,21 +321,23 @@ const RoomSubList = React.createClass({
|
||||
|
||||
render: function() {
|
||||
const len = this.props.list.length + this.props.extraTiles.length;
|
||||
const isCollapsed = this.state.hidden && !this.props.forceExpand;
|
||||
if (len) {
|
||||
const subListClasses = classNames({
|
||||
"mx_RoomSubList": true,
|
||||
"mx_RoomSubList_hidden": this.state.hidden,
|
||||
"mx_RoomSubList_nonEmpty": len && !this.state.hidden,
|
||||
"mx_RoomSubList_hidden": isCollapsed,
|
||||
"mx_RoomSubList_nonEmpty": len && !isCollapsed,
|
||||
});
|
||||
if (this.state.hidden) {
|
||||
|
||||
if (isCollapsed) {
|
||||
return <div ref="subList" className={subListClasses}>
|
||||
{this._getHeaderJsx()}
|
||||
{this._getHeaderJsx(isCollapsed)}
|
||||
</div>;
|
||||
} else {
|
||||
const tiles = this.makeRoomTiles();
|
||||
tiles.push(...this.props.extraTiles);
|
||||
return <div ref="subList" className={subListClasses}>
|
||||
{this._getHeaderJsx()}
|
||||
{this._getHeaderJsx(isCollapsed)}
|
||||
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll">
|
||||
{ tiles }
|
||||
</IndicatorScrollbar>
|
||||
@ -344,13 +346,13 @@ const RoomSubList = React.createClass({
|
||||
} else {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
let content;
|
||||
if (this.props.showSpinner && !this.state.hidden) {
|
||||
if (this.props.showSpinner && !isCollapsed) {
|
||||
content = <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref="subList" className="mx_RoomSubList">
|
||||
{ this._getHeaderJsx() }
|
||||
{ this._getHeaderJsx(isCollapsed) }
|
||||
{ content }
|
||||
</div>
|
||||
);
|
||||
|
@ -211,7 +211,6 @@ module.exports = React.createClass({
|
||||
if (!SdkConfig.get()['disable_custom_urls']) {
|
||||
serverConfigSection = (
|
||||
<ServerConfig ref="serverConfig"
|
||||
withToggleButton={true}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
defaultIsUrl={this.props.defaultIsUrl}
|
||||
customHsUrl={this.props.customHsUrl}
|
||||
|
@ -26,10 +26,20 @@ import Login from '../../../Login';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||
import { AutoDiscovery } from "matrix-js-sdk";
|
||||
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
||||
|
||||
// For validating phone numbers without country codes
|
||||
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
|
||||
|
||||
// Phases
|
||||
// Show controls to configure server details
|
||||
const PHASE_SERVER_DETAILS = 0;
|
||||
// Show the appropriate login flow(s) for the server
|
||||
const PHASE_LOGIN = 1;
|
||||
|
||||
// Disable phases for now, pending UX discussion on WK discovery
|
||||
const PHASES_ENABLED = false;
|
||||
|
||||
// These are used in several places, and come from the js-sdk's autodiscovery
|
||||
// stuff. We define them here so that they'll be picked up by i18n.
|
||||
_td("Invalid homeserver discovery response");
|
||||
@ -80,6 +90,8 @@ module.exports = React.createClass({
|
||||
busy: false,
|
||||
errorText: null,
|
||||
loginIncorrect: false,
|
||||
|
||||
serverType: null,
|
||||
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
||||
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
||||
|
||||
@ -87,6 +99,10 @@ module.exports = React.createClass({
|
||||
username: "",
|
||||
phoneCountry: null,
|
||||
phoneNumber: "",
|
||||
|
||||
// Phase of the overall login dialog.
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
// The current login flow, such as password, SSO, etc.
|
||||
currentFlow: "m.login.password",
|
||||
|
||||
// .well-known discovery
|
||||
@ -299,12 +315,60 @@ module.exports = React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
onServerTypeChange(type) {
|
||||
this.setState({
|
||||
serverType: type,
|
||||
});
|
||||
|
||||
// When changing server types, set the HS / IS URLs to reasonable defaults for the
|
||||
// the new type.
|
||||
switch (type) {
|
||||
case ServerType.FREE: {
|
||||
const { hsUrl, isUrl } = ServerType.TYPES.FREE;
|
||||
this.onServerConfigChange({
|
||||
hsUrl,
|
||||
isUrl,
|
||||
});
|
||||
// Move directly to the login phase since the server details are fixed.
|
||||
this.setState({
|
||||
phase: PHASE_LOGIN,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ServerType.PREMIUM:
|
||||
case ServerType.ADVANCED:
|
||||
this.onServerConfigChange({
|
||||
hsUrl: this.props.defaultHsUrl,
|
||||
isUrl: this.props.defaultIsUrl,
|
||||
});
|
||||
this.setState({
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onRegisterClick: function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onRegisterClick();
|
||||
},
|
||||
|
||||
onServerDetailsNextPhaseClick(ev) {
|
||||
ev.stopPropagation();
|
||||
this.setState({
|
||||
phase: PHASE_LOGIN,
|
||||
});
|
||||
},
|
||||
|
||||
onEditServerDetailsClick(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.setState({
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
});
|
||||
},
|
||||
|
||||
_tryWellKnownDiscovery: async function(serverName) {
|
||||
if (!serverName.trim()) {
|
||||
// Nothing to discover
|
||||
@ -475,7 +539,79 @@ module.exports = React.createClass({
|
||||
return errorText;
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
serverComponentForStep() {
|
||||
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
|
||||
// TODO: May need to adjust the behavior of this config option
|
||||
if (SdkConfig.get()['disable_custom_urls']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we're on a different phase, we only show the server type selector,
|
||||
// which is always shown if we allow custom URLs at all.
|
||||
if (PHASES_ENABLED && this.state.phase !== PHASE_SERVER_DETAILS) {
|
||||
return <div>
|
||||
<ServerTypeSelector
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
onChange={this.onServerTypeChange}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
let serverDetails = null;
|
||||
switch (this.state.serverType) {
|
||||
case ServerType.FREE:
|
||||
break;
|
||||
case ServerType.PREMIUM:
|
||||
serverDetails = <ModularServerConfig
|
||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
defaultIsUrl={this.props.defaultIsUrl}
|
||||
onServerConfigChange={this.onServerConfigChange}
|
||||
delayTimeMs={1000}
|
||||
/>;
|
||||
break;
|
||||
case ServerType.ADVANCED:
|
||||
serverDetails = <ServerConfig
|
||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
defaultIsUrl={this.props.defaultIsUrl}
|
||||
onServerConfigChange={this.onServerConfigChange}
|
||||
delayTimeMs={1000}
|
||||
/>;
|
||||
break;
|
||||
}
|
||||
|
||||
let nextButton = null;
|
||||
if (PHASES_ENABLED) {
|
||||
nextButton = <AccessibleButton className="mx_Login_submit"
|
||||
onClick={this.onServerDetailsNextPhaseClick}
|
||||
>
|
||||
{_t("Next")}
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<ServerTypeSelector
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
onChange={this.onServerTypeChange}
|
||||
/>
|
||||
{serverDetails}
|
||||
{nextButton}
|
||||
</div>;
|
||||
},
|
||||
|
||||
loginComponentForStep() {
|
||||
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const step = this.state.currentFlow;
|
||||
|
||||
if (!step) {
|
||||
return null;
|
||||
}
|
||||
@ -491,10 +627,21 @@ module.exports = React.createClass({
|
||||
|
||||
_renderPasswordStep: function() {
|
||||
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
||||
let onEditServerDetailsClick = null;
|
||||
// If custom URLs are allowed and we haven't selected the Free server type, wire
|
||||
// up the server details edit link.
|
||||
if (
|
||||
PHASES_ENABLED &&
|
||||
!SdkConfig.get()['disable_custom_urls'] &&
|
||||
this.state.serverType !== ServerType.FREE
|
||||
) {
|
||||
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
||||
}
|
||||
return (
|
||||
<PasswordLogin
|
||||
onSubmit={this.onPasswordLogin}
|
||||
onError={this.onPasswordLoginError}
|
||||
onEditServerDetailsClick={onEditServerDetailsClick}
|
||||
initialUsername={this.state.username}
|
||||
initialPhoneCountry={this.state.phoneCountry}
|
||||
initialPhoneNumber={this.state.phoneNumber}
|
||||
@ -521,7 +668,7 @@ module.exports = React.createClass({
|
||||
// user's browser, let them log into their SSO provider, then redirect their browser
|
||||
// to vector://vector which, of course, will not work.
|
||||
return (
|
||||
<a href={url} className="mx_Login_sso_link">{ _t('Sign in with single sign-on') }</a>
|
||||
<a href={url} className="mx_Login_sso_link mx_Login_submit">{ _t('Sign in with single sign-on') }</a>
|
||||
);
|
||||
},
|
||||
|
||||
@ -530,7 +677,6 @@ module.exports = React.createClass({
|
||||
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
|
||||
|
||||
const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText;
|
||||
@ -543,19 +689,6 @@ module.exports = React.createClass({
|
||||
</a>;
|
||||
}
|
||||
|
||||
let serverConfig;
|
||||
|
||||
if (!SdkConfig.get()['disable_custom_urls']) {
|
||||
serverConfig = <ServerConfig ref="serverConfig"
|
||||
withToggleButton={true}
|
||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
defaultIsUrl={this.props.defaultIsUrl}
|
||||
onServerConfigChange={this.onServerConfigChange}
|
||||
delayTimeMs={1000} />;
|
||||
}
|
||||
|
||||
let errorTextSection;
|
||||
if (errorText) {
|
||||
errorTextSection = (
|
||||
@ -574,8 +707,8 @@ module.exports = React.createClass({
|
||||
{loader}
|
||||
</h2>
|
||||
{ errorTextSection }
|
||||
{ this.componentForStep(this.state.currentFlow) }
|
||||
{ serverConfig }
|
||||
{ this.serverComponentForStep() }
|
||||
{ this.loginComponentForStep() }
|
||||
<a className="mx_Auth_changeFlow" onClick={this.onRegisterClick} href="#">
|
||||
{ _t('Create account') }
|
||||
</a>
|
||||
|
@ -255,7 +255,7 @@ module.exports = React.createClass({
|
||||
errMsg = _t("Only use lower case letters, numbers and '=_-./'");
|
||||
break;
|
||||
case "RegistrationForm.ERR_USERNAME_BLANK":
|
||||
errMsg = _t('You need to enter a user name.');
|
||||
errMsg = _t('You need to enter a username.');
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown error code: %s", errCode);
|
||||
@ -330,7 +330,6 @@ module.exports = React.createClass({
|
||||
if (!SdkConfig.get()['disable_custom_urls']) {
|
||||
serverConfigSection = (
|
||||
<ServerConfig ref="serverConfig"
|
||||
withToggleButton={true}
|
||||
customHsUrl={this.props.customHsUrl}
|
||||
customIsUrl={this.props.customIsUrl}
|
||||
defaultHsUrl={this.props.defaultHsUrl}
|
||||
|
@ -27,17 +27,17 @@ module.exports = React.createClass({
|
||||
{ _t("Custom Server Options") }
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<span>
|
||||
{ _t("You can use the custom server options to sign into other Matrix " +
|
||||
"servers by specifying a different Home server URL.") }
|
||||
<br />
|
||||
{ _t("This allows you to use this app with an existing Matrix account on " +
|
||||
"a different home server.") }
|
||||
<br />
|
||||
<br />
|
||||
{ _t("You can also set a custom identity server but this will typically prevent " +
|
||||
"interaction with users based on email address.") }
|
||||
</span>
|
||||
<p>{_t(
|
||||
"You can use the custom server options to sign into other " +
|
||||
"Matrix servers by specifying a different homeserver URL. This " +
|
||||
"allows you to use this app with an existing Matrix account on a " +
|
||||
"different homeserver.",
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"You can also set a custom identity server, but you won't be " +
|
||||
"able to invite users by email address, or be invited by email " +
|
||||
"address yourself.",
|
||||
)}</p>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.props.onFinished} autoFocus={true}>
|
||||
|
126
src/components/views/auth/ModularServerConfig.js
Normal file
126
src/components/views/auth/ModularServerConfig.js
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
|
||||
|
||||
/*
|
||||
* Configure the Modular server name.
|
||||
*
|
||||
* This is a variant of ServerConfig with only the HS field and different body
|
||||
* text that is specific to the Modular case.
|
||||
*/
|
||||
export default class ModularServerConfig extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onServerConfigChange: PropTypes.func,
|
||||
|
||||
// default URLs are defined in config.json (or the hardcoded defaults)
|
||||
// they are used if the user has not overridden them with a custom URL.
|
||||
// In other words, if the custom URL is blank, the default is used.
|
||||
defaultHsUrl: PropTypes.string, // e.g. https://matrix.org
|
||||
|
||||
// This component always uses the default IS URL and doesn't allow it
|
||||
// to be changed. We still receive it as a prop here to simplify
|
||||
// consumers by still passing the IS URL via onServerConfigChange.
|
||||
defaultIsUrl: PropTypes.string, // e.g. https://vector.im
|
||||
|
||||
// custom URLs are explicitly provided by the user and override the
|
||||
// default URLs. The user enters them via the component's input fields,
|
||||
// which is reflected on these properties whenever on..UrlChanged fires.
|
||||
// They are persisted in localStorage by MatrixClientPeg, and so can
|
||||
// override the default URLs when the component initially loads.
|
||||
customHsUrl: PropTypes.string,
|
||||
|
||||
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onServerConfigChange: function() {},
|
||||
customHsUrl: "",
|
||||
delayTimeMs: 0,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hsUrl: props.customHsUrl,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (newProps.customHsUrl === this.state.hsUrl) return;
|
||||
|
||||
this.setState({
|
||||
hsUrl: newProps.customHsUrl,
|
||||
});
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: newProps.customHsUrl,
|
||||
isUrl: this.props.defaultIsUrl,
|
||||
});
|
||||
}
|
||||
|
||||
onHomeserverChanged = (ev) => {
|
||||
this.setState({hsUrl: ev.target.value}, () => {
|
||||
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
|
||||
let hsUrl = this.state.hsUrl.trim().replace(/\/$/, "");
|
||||
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: this.state.hsUrl,
|
||||
isUrl: this.props.defaultIsUrl,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_waitThenInvoke(existingTimeoutId, fn) {
|
||||
if (existingTimeoutId) {
|
||||
clearTimeout(existingTimeoutId);
|
||||
}
|
||||
return setTimeout(fn.bind(this), this.props.delayTimeMs);
|
||||
}
|
||||
|
||||
render() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
return (
|
||||
<div className="mx_ServerConfig">
|
||||
<h3>{_t("Your Modular server")}</h3>
|
||||
{_t(
|
||||
"Enter the location of your Modular homeserver. It may use your own " +
|
||||
"domain name or be a subdomain of <a>modular.im</a>.",
|
||||
{}, {
|
||||
a: sub => <a href={MODULAR_URL} target="_blank" rel="noopener">
|
||||
{sub}
|
||||
</a>,
|
||||
},
|
||||
)}
|
||||
<div className="mx_ServerConfig_fields">
|
||||
<Field id="mx_ServerConfig_hsUrl"
|
||||
label={_t("Server Name")}
|
||||
placeholder={this.props.defaultHsUrl}
|
||||
value={this.state.hsUrl}
|
||||
onChange={this.onHomeserverChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import SdkConfig from '../../../SdkConfig';
|
||||
class PasswordLogin extends React.Component {
|
||||
static defaultProps = {
|
||||
onError: function() {},
|
||||
onEditServerDetailsClick: null,
|
||||
onUsernameChanged: function() {},
|
||||
onUsernameBlur: function() {},
|
||||
onPasswordChanged: function() {},
|
||||
@ -93,7 +94,7 @@ class PasswordLogin extends React.Component {
|
||||
case PasswordLogin.LOGIN_FIELD_MXID:
|
||||
username = this.state.username;
|
||||
if (!username) {
|
||||
error = _t('The user name field must not be blank.');
|
||||
error = _t('The username field must not be blank.');
|
||||
}
|
||||
break;
|
||||
case PasswordLogin.LOGIN_FIELD_PHONE:
|
||||
@ -158,18 +159,16 @@ class PasswordLogin extends React.Component {
|
||||
this.props.onPasswordChanged(ev.target.value);
|
||||
}
|
||||
|
||||
renderLoginField(loginType, disabled) {
|
||||
renderLoginField(loginType) {
|
||||
const classes = {
|
||||
mx_Login_field: true,
|
||||
mx_Login_field_disabled: disabled,
|
||||
};
|
||||
|
||||
switch (loginType) {
|
||||
case PasswordLogin.LOGIN_FIELD_EMAIL:
|
||||
classes.mx_Login_email = true;
|
||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||
return <input
|
||||
className={classNames(classes)}
|
||||
className="mx_Login_field"
|
||||
ref={(e) => {this._loginField = e;}}
|
||||
key="email_input"
|
||||
type="text"
|
||||
@ -179,10 +178,8 @@ class PasswordLogin extends React.Component {
|
||||
placeholder="joe@example.com"
|
||||
value={this.state.username}
|
||||
autoFocus
|
||||
disabled={disabled}
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_MXID:
|
||||
classes.mx_Login_username = true;
|
||||
classes.error = this.props.loginIncorrect && !this.state.username;
|
||||
return <input
|
||||
className={classNames(classes)}
|
||||
@ -195,14 +192,12 @@ class PasswordLogin extends React.Component {
|
||||
placeholder={SdkConfig.get().disable_custom_urls ?
|
||||
_t("Username on %(hs)s", {
|
||||
hs: this.props.hsUrl.replace(/^https?:\/\//, ''),
|
||||
}) : _t("User name")}
|
||||
}) : _t("Username")}
|
||||
value={this.state.username}
|
||||
autoFocus
|
||||
disabled={disabled}
|
||||
/>;
|
||||
case PasswordLogin.LOGIN_FIELD_PHONE: {
|
||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||
classes.mx_Login_phoneNumberField = true;
|
||||
classes.mx_Login_field_has_prefix = true;
|
||||
classes.error = this.props.loginIncorrect && !this.state.phoneNumber;
|
||||
return <div className="mx_Login_phoneSection">
|
||||
@ -212,7 +207,6 @@ class PasswordLogin extends React.Component {
|
||||
value={this.state.phoneCountry}
|
||||
isSmall={true}
|
||||
showPrefix={true}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<input
|
||||
className={classNames(classes)}
|
||||
@ -224,7 +218,6 @@ class PasswordLogin extends React.Component {
|
||||
placeholder={_t("Mobile phone number")}
|
||||
value={this.state.phoneNumber}
|
||||
autoFocus
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
@ -257,27 +250,35 @@ class PasswordLogin extends React.Component {
|
||||
</span>;
|
||||
}
|
||||
|
||||
let matrixIdText = _t('Matrix ID');
|
||||
let yourMatrixAccountText = _t('Your account');
|
||||
if (this.props.hsName) {
|
||||
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: this.props.hsName});
|
||||
yourMatrixAccountText = _t('Your %(serverName)s account', {serverName: this.props.hsName});
|
||||
} else {
|
||||
try {
|
||||
const parsedHsUrl = new URL(this.props.hsUrl);
|
||||
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
|
||||
yourMatrixAccountText = _t('Your %(serverName)s account', {serverName: parsedHsUrl.hostname});
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
let editLink = null;
|
||||
if (this.props.onEditServerDetailsClick) {
|
||||
editLink = <a className="mx_Auth_editServerDetails"
|
||||
href="#" onClick={this.props.onEditServerDetailsClick}
|
||||
>
|
||||
{_t('Edit')}
|
||||
</a>;
|
||||
}
|
||||
|
||||
const pwFieldClass = classNames({
|
||||
mx_Login_field: true,
|
||||
mx_Login_field_disabled: matrixIdText === '',
|
||||
error: this.props.loginIncorrect && !this.isLoginEmpty(), // only error password if error isn't top field
|
||||
});
|
||||
|
||||
const Dropdown = sdk.getComponent('elements.Dropdown');
|
||||
|
||||
const loginField = this.renderLoginField(this.state.loginType, matrixIdText === '');
|
||||
const loginField = this.renderLoginField(this.state.loginType);
|
||||
|
||||
let loginType;
|
||||
if (!SdkConfig.get().disable_3pid_login) {
|
||||
@ -287,9 +288,8 @@ class PasswordLogin extends React.Component {
|
||||
<Dropdown
|
||||
className="mx_Login_type_dropdown"
|
||||
value={this.state.loginType}
|
||||
disabled={matrixIdText === ''}
|
||||
onOptionChange={this.onLoginTypeChange}>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ matrixIdText }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('Username') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
|
||||
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
|
||||
</Dropdown>
|
||||
@ -297,22 +297,27 @@ class PasswordLogin extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const disableSubmit = this.props.disableSubmit || matrixIdText === '';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>
|
||||
{yourMatrixAccountText}
|
||||
{editLink}
|
||||
</h3>
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
{ loginType }
|
||||
{ loginField }
|
||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||
name="password"
|
||||
value={this.state.password} onChange={this.onPasswordChanged}
|
||||
placeholder={_t('Password')}
|
||||
disabled={matrixIdText === ''}
|
||||
/>
|
||||
<br />
|
||||
{ forgotPasswordJsx }
|
||||
<input className="mx_Login_submit" type="submit" value={_t('Sign in')} disabled={disableSubmit} />
|
||||
{ loginType }
|
||||
{ loginField }
|
||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||
name="password"
|
||||
value={this.state.password} onChange={this.onPasswordChanged}
|
||||
placeholder={_t('Password')}
|
||||
/>
|
||||
<br />
|
||||
{ forgotPasswordJsx }
|
||||
<input className="mx_Login_submit"
|
||||
type="submit"
|
||||
value={_t('Sign in')}
|
||||
disabled={this.props.disableSubmit}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
@ -305,7 +305,7 @@ module.exports = React.createClass({
|
||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
|
||||
);
|
||||
|
||||
const placeholderUserName = _t("User name");
|
||||
const placeholderUsername = _t("Username");
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -313,7 +313,7 @@ module.exports = React.createClass({
|
||||
{ emailSection }
|
||||
{ phoneSection }
|
||||
<input type="text" ref="username"
|
||||
placeholder={placeholderUserName} defaultValue={this.props.defaultUsername}
|
||||
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
||||
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
||||
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
|
||||
<br />
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -14,21 +15,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
const Modal = require('../../../Modal');
|
||||
const sdk = require('../../../index');
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
/**
|
||||
/*
|
||||
* A pure UI component which displays the HS and IS to use.
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ServerConfig',
|
||||
|
||||
propTypes: {
|
||||
export default class ServerConfig extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onServerConfigChange: PropTypes.func,
|
||||
|
||||
// default URLs are defined in config.json (or the hardcoded defaults)
|
||||
@ -45,155 +43,103 @@ module.exports = React.createClass({
|
||||
customHsUrl: PropTypes.string,
|
||||
customIsUrl: PropTypes.string,
|
||||
|
||||
withToggleButton: PropTypes.bool,
|
||||
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
|
||||
},
|
||||
}
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onServerConfigChange: function() {},
|
||||
customHsUrl: "",
|
||||
customIsUrl: "",
|
||||
withToggleButton: false,
|
||||
delayTimeMs: 0,
|
||||
static defaultProps = {
|
||||
onServerConfigChange: function() {},
|
||||
customHsUrl: "",
|
||||
customIsUrl: "",
|
||||
delayTimeMs: 0,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hsUrl: props.customHsUrl,
|
||||
isUrl: props.customIsUrl,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
hs_url: this.props.customHsUrl,
|
||||
is_url: this.props.customIsUrl,
|
||||
// if withToggleButton is false, then show the config all the time given we have no way otherwise of making it visible
|
||||
configVisible: !this.props.withToggleButton ||
|
||||
(this.props.customHsUrl !== this.props.defaultHsUrl) ||
|
||||
(this.props.customIsUrl !== this.props.defaultIsUrl),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
if (newProps.customHsUrl === this.state.hs_url &&
|
||||
newProps.customIsUrl === this.state.is_url) return;
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (newProps.customHsUrl === this.state.hsUrl &&
|
||||
newProps.customIsUrl === this.state.isUrl) return;
|
||||
|
||||
this.setState({
|
||||
hs_url: newProps.customHsUrl,
|
||||
is_url: newProps.customIsUrl,
|
||||
configVisible: !newProps.withToggleButton ||
|
||||
(newProps.customHsUrl !== newProps.defaultHsUrl) ||
|
||||
(newProps.customIsUrl !== newProps.defaultIsUrl),
|
||||
hsUrl: newProps.customHsUrl,
|
||||
isUrl: newProps.customIsUrl,
|
||||
});
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: newProps.customHsUrl,
|
||||
isUrl: newProps.customIsUrl,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onHomeserverChanged: function(ev) {
|
||||
this.setState({hs_url: ev.target.value}, () => {
|
||||
onHomeserverChanged = (ev) => {
|
||||
this.setState({hsUrl: ev.target.value}, () => {
|
||||
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
|
||||
let hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
|
||||
let hsUrl = this.state.hsUrl.trim().replace(/\/$/, "");
|
||||
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: this.state.hs_url,
|
||||
isUrl: this.state.is_url,
|
||||
hsUrl: this.state.hsUrl,
|
||||
isUrl: this.state.isUrl,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onIdentityServerChanged: function(ev) {
|
||||
this.setState({is_url: ev.target.value}, () => {
|
||||
onIdentityServerChanged = (ev) => {
|
||||
this.setState({isUrl: ev.target.value}, () => {
|
||||
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => {
|
||||
let isUrl = this.state.is_url.trim().replace(/\/$/, "");
|
||||
let isUrl = this.state.isUrl.trim().replace(/\/$/, "");
|
||||
if (isUrl === "") isUrl = this.props.defaultIsUrl;
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: this.state.hs_url,
|
||||
isUrl: this.state.is_url,
|
||||
hsUrl: this.state.hsUrl,
|
||||
isUrl: this.state.isUrl,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_waitThenInvoke: function(existingTimeoutId, fn) {
|
||||
_waitThenInvoke(existingTimeoutId, fn) {
|
||||
if (existingTimeoutId) {
|
||||
clearTimeout(existingTimeoutId);
|
||||
}
|
||||
return setTimeout(fn.bind(this), this.props.delayTimeMs);
|
||||
},
|
||||
}
|
||||
|
||||
onServerConfigVisibleChange: function(visible, ev) {
|
||||
this.setState({
|
||||
configVisible: visible,
|
||||
});
|
||||
if (!visible) {
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: this.props.defaultHsUrl,
|
||||
isUrl: this.props.defaultIsUrl,
|
||||
});
|
||||
} else {
|
||||
this.props.onServerConfigChange({
|
||||
hsUrl: this.state.hs_url,
|
||||
isUrl: this.state.is_url,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showHelpPopup: function() {
|
||||
showHelpPopup = () => {
|
||||
const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog');
|
||||
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
const serverConfigStyle = {};
|
||||
serverConfigStyle.display = this.state.configVisible ? 'block' : 'none';
|
||||
|
||||
let toggleButton;
|
||||
if (this.props.withToggleButton) {
|
||||
toggleButton = (
|
||||
<div className="mx_ServerConfig_selector">
|
||||
<input className="mx_Login_radio" id="basic" name="configVisible" type="radio"
|
||||
checked={!this.state.configVisible}
|
||||
onChange={this.onServerConfigVisibleChange.bind(this, false)} />
|
||||
<label className="mx_Login_label" htmlFor="basic">
|
||||
{ _t("Default server") }
|
||||
</label>
|
||||
|
||||
<input className="mx_Login_radio" id="advanced" name="configVisible" type="radio"
|
||||
checked={this.state.configVisible}
|
||||
onChange={this.onServerConfigVisibleChange.bind(this, true)} />
|
||||
<label className="mx_Login_label" htmlFor="advanced">
|
||||
{ _t("Custom server") }
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ toggleButton }
|
||||
<div style={serverConfigStyle}>
|
||||
<div className="mx_ServerConfig">
|
||||
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">
|
||||
{ _t("Home server URL") }
|
||||
</label>
|
||||
<input className="mx_Login_field" id="hsurl" type="text"
|
||||
<div className="mx_ServerConfig">
|
||||
<h3>{_t("Other servers")}</h3>
|
||||
{_t("Enter custom server URLs <a>What does this mean?</a>", {}, {
|
||||
a: sub => <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
|
||||
{ sub }
|
||||
</a>,
|
||||
})}
|
||||
<div className="mx_ServerConfig_fields">
|
||||
<Field id="mx_ServerConfig_hsUrl"
|
||||
label={_t("Homeserver URL")}
|
||||
placeholder={this.props.defaultHsUrl}
|
||||
disabled={!this.props.withToggleButton}
|
||||
value={this.state.hs_url}
|
||||
onChange={this.onHomeserverChanged} />
|
||||
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">
|
||||
{ _t("Identity server URL") }
|
||||
</label>
|
||||
<input className="mx_Login_field" id="isurl" type="text"
|
||||
value={this.state.hsUrl}
|
||||
onChange={this.onHomeserverChanged}
|
||||
/>
|
||||
<Field id="mx_ServerConfig_isUrl"
|
||||
label={_t("Identity Server URL")}
|
||||
placeholder={this.props.defaultIsUrl}
|
||||
disabled={!this.props.withToggleButton}
|
||||
value={this.state.is_url}
|
||||
onChange={this.onIdentityServerChanged} />
|
||||
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
|
||||
{ _t("What does this mean?") }
|
||||
</a>
|
||||
value={this.state.isUrl}
|
||||
onChange={this.onIdentityServerChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
148
src/components/views/auth/ServerTypeSelector.js
Normal file
148
src/components/views/auth/ServerTypeSelector.js
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
|
||||
|
||||
export const FREE = 'Free';
|
||||
export const PREMIUM = 'Premium';
|
||||
export const ADVANCED = 'Advanced';
|
||||
|
||||
export const TYPES = {
|
||||
FREE: {
|
||||
id: FREE,
|
||||
label: () => _t('Free'),
|
||||
logo: () => <img src={require('../../../../res/img/feather-icons/matrix-org-bw-logo.svg')} />,
|
||||
description: () => _t('Join millions for free on the largest public server'),
|
||||
hsUrl: 'https://matrix.org',
|
||||
isUrl: 'https://vector.im',
|
||||
},
|
||||
PREMIUM: {
|
||||
id: PREMIUM,
|
||||
label: () => _t('Premium'),
|
||||
logo: () => <img src={require('../../../../res/img/feather-icons/modular-bw-logo.svg')} />,
|
||||
description: () => _t('Premium hosting for organisations <a>Learn more</a>', {}, {
|
||||
a: sub => <a href={MODULAR_URL} target="_blank" rel="noopener">
|
||||
{sub}
|
||||
</a>,
|
||||
}),
|
||||
},
|
||||
ADVANCED: {
|
||||
id: ADVANCED,
|
||||
label: () => _t('Advanced'),
|
||||
logo: () => <div>
|
||||
<img src={require('../../../../res/img/feather-icons/globe.svg')} />
|
||||
{_t('Other')}
|
||||
</div>,
|
||||
description: () => _t('Find other public servers or use a custom server'),
|
||||
},
|
||||
};
|
||||
|
||||
function getDefaultType(defaultHsUrl) {
|
||||
if (!defaultHsUrl) {
|
||||
return null;
|
||||
} else if (defaultHsUrl === TYPES.FREE.hsUrl) {
|
||||
return FREE;
|
||||
} else if (new URL(defaultHsUrl).hostname.endsWith('.modular.im')) {
|
||||
// TODO: Use a Riot config parameter to detect Modular-ness.
|
||||
// https://github.com/vector-im/riot-web/issues/8253
|
||||
return PREMIUM;
|
||||
} else {
|
||||
return ADVANCED;
|
||||
}
|
||||
}
|
||||
|
||||
export default class ServerTypeSelector extends React.PureComponent {
|
||||
static propTypes = {
|
||||
// The default HS URL as another way to set the initially selected type.
|
||||
defaultHsUrl: PropTypes.string,
|
||||
// Handler called when the selected type changes.
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {
|
||||
defaultHsUrl,
|
||||
onChange,
|
||||
} = props;
|
||||
const type = getDefaultType(defaultHsUrl);
|
||||
this.state = {
|
||||
selected: type,
|
||||
};
|
||||
if (onChange) {
|
||||
onChange(type);
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedType(type) {
|
||||
if (this.state.selected === type) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
selected: type,
|
||||
});
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(type);
|
||||
}
|
||||
}
|
||||
|
||||
onClick = (e) => {
|
||||
e.stopPropagation();
|
||||
const type = e.currentTarget.dataset.id;
|
||||
this.updateSelectedType(type);
|
||||
}
|
||||
|
||||
render() {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
const serverTypes = [];
|
||||
for (const type of Object.values(TYPES)) {
|
||||
const { id, label, logo, description } = type;
|
||||
const classes = classnames(
|
||||
"mx_ServerTypeSelector_type",
|
||||
`mx_ServerTypeSelector_type_${id}`,
|
||||
{
|
||||
"mx_ServerTypeSelector_type_selected": id === this.state.selected,
|
||||
},
|
||||
);
|
||||
|
||||
serverTypes.push(<div className={classes} key={id} >
|
||||
<div className="mx_ServerTypeSelector_label">
|
||||
{label()}
|
||||
</div>
|
||||
<AccessibleButton onClick={this.onClick} data-id={id}>
|
||||
<div className="mx_ServerTypeSelector_logo">
|
||||
{logo()}
|
||||
</div>
|
||||
<div className="mx_ServerTypeSelector_description">
|
||||
{description()}
|
||||
</div>
|
||||
</AccessibleButton>
|
||||
</div>);
|
||||
}
|
||||
|
||||
return <div className="mx_ServerTypeSelector">
|
||||
{serverTypes}
|
||||
</div>;
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ import NotificationSettingsTab from "../settings/tabs/NotificationSettingsTab";
|
||||
import PreferencesSettingsTab from "../settings/tabs/PreferencesSettingsTab";
|
||||
import VoiceSettingsTab from "../settings/tabs/VoiceSettingsTab";
|
||||
import HelpSettingsTab from "../settings/tabs/HelpSettingsTab";
|
||||
import FlairSettingsTab from "../settings/tabs/FlairSettingsTab";
|
||||
|
||||
// TODO: Ditch this whole component
|
||||
export class TempTab extends React.Component {
|
||||
@ -58,6 +59,11 @@ export default class UserSettingsDialog extends React.Component {
|
||||
"mx_UserSettingsDialog_settingsIcon",
|
||||
<GeneralUserSettingsTab />,
|
||||
));
|
||||
tabs.push(new Tab(
|
||||
_td("Flair"),
|
||||
"mx_UserSettingsDialog_flairIcon",
|
||||
<FlairSettingsTab />,
|
||||
));
|
||||
tabs.push(new Tab(
|
||||
_td("Notifications"),
|
||||
"mx_UserSettingsDialog_bellIcon",
|
||||
|
@ -18,7 +18,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import GroupStore from '../../../stores/GroupStore';
|
||||
import { _t } from '../../../languageHandler.js';
|
||||
import ToggleSwitch from "../elements/ToggleSwitch";
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'GroupPublicityToggle',
|
||||
@ -52,8 +52,7 @@ export default React.createClass({
|
||||
if (this._groupStoreToken) this._groupStoreToken.unregister();
|
||||
},
|
||||
|
||||
_onPublicityToggle: function(e) {
|
||||
e.stopPropagation();
|
||||
_onPublicityToggle: function() {
|
||||
this.setState({
|
||||
busy: true,
|
||||
// Optimistic early update
|
||||
@ -68,21 +67,11 @@ export default React.createClass({
|
||||
|
||||
render() {
|
||||
const GroupTile = sdk.getComponent('groups.GroupTile');
|
||||
const input = <input type="checkbox"
|
||||
onChange={this._onPublicityToggle}
|
||||
checked={this.state.isGroupPublicised}
|
||||
/>;
|
||||
const labelText = !this.state.ready ? _t("Loading...") :
|
||||
(this.state.isGroupPublicised ?
|
||||
_t("Flair will appear if enabled in room settings") :
|
||||
_t("Flair will not appear")
|
||||
);
|
||||
return <div className="mx_GroupPublicity_toggle">
|
||||
<GroupTile groupId={this.props.groupId} showDescription={false} avatarHeight={40} />
|
||||
<label onClick={this._onPublicityToggle}>
|
||||
{ input }
|
||||
{ labelText }
|
||||
</label>
|
||||
<ToggleSwitch checked={this.state.isGroupPublicised}
|
||||
disabled={!this.state.ready || this.state.busy}
|
||||
onChange={this._onPublicityToggle} />
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
|
@ -43,9 +43,9 @@ export default React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
_renderGroupPublicity() {
|
||||
render() {
|
||||
let text = "";
|
||||
let scrollbox = <div />;
|
||||
let groupPublicityToggles = null;
|
||||
const groups = this.state.groups;
|
||||
|
||||
if (this.state.error) {
|
||||
@ -54,16 +54,10 @@ export default React.createClass({
|
||||
text = _t('Loading...');
|
||||
} else if (groups.length > 0) {
|
||||
const GroupPublicityToggle = sdk.getComponent('groups.GroupPublicityToggle');
|
||||
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
|
||||
const groupPublicityToggles = groups.map((groupId, index) => {
|
||||
groupPublicityToggles = groups.map((groupId, index) => {
|
||||
return <GroupPublicityToggle key={index} groupId={groupId} />;
|
||||
});
|
||||
text = _t('Display your community flair in rooms configured to show it.');
|
||||
scrollbox = <div className="mx_GroupUserSettings_groupPublicity_scrollbox">
|
||||
<GeminiScrollbarWrapper>
|
||||
{ groupPublicityToggles }
|
||||
</GeminiScrollbarWrapper>
|
||||
</div>;
|
||||
} else {
|
||||
text = _t("You're not currently a member of any communities.");
|
||||
}
|
||||
@ -71,16 +65,10 @@ export default React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<p className="mx_SettingsTab_subsectionText">{ text }</p>
|
||||
{ scrollbox }
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
{ groupPublicityToggles }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
const groupPublicity = this._renderGroupPublicity();
|
||||
|
||||
return <div>
|
||||
{ groupPublicity }
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
|
@ -82,7 +82,11 @@ module.exports = React.createClass({
|
||||
this.collapsedState = collapsedJson ? JSON.parse(collapsedJson) : {};
|
||||
this._layoutSections = [];
|
||||
|
||||
this._layout = new Layout((key, size) => {
|
||||
const unfilteredOptions = {
|
||||
allowWhitespace: true,
|
||||
handleHeight: 1,
|
||||
};
|
||||
this._unfilteredlayout = new Layout((key, size) => {
|
||||
const subList = this._subListRefs[key];
|
||||
if (subList) {
|
||||
subList.setHeight(size);
|
||||
@ -95,7 +99,19 @@ module.exports = React.createClass({
|
||||
window.localStorage.setItem("mx_roomlist_sizes",
|
||||
JSON.stringify(this.subListSizes));
|
||||
}
|
||||
}, this.subListSizes, this.collapsedState);
|
||||
}, this.subListSizes, this.collapsedState, unfilteredOptions);
|
||||
|
||||
this._filteredLayout = new Layout((key, size) => {
|
||||
const subList = this._subListRefs[key];
|
||||
if (subList) {
|
||||
subList.setHeight(size);
|
||||
}
|
||||
}, null, null, {
|
||||
allowWhitespace: false,
|
||||
handleHeight: 0,
|
||||
});
|
||||
|
||||
this._layout = this._unfilteredlayout;
|
||||
|
||||
return {
|
||||
isLoadingLeftRooms: false,
|
||||
@ -187,15 +203,21 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
componentDidUpdate: function(prevProps) {
|
||||
let forceLayoutUpdate = false;
|
||||
this._repositionIncomingCallBox(undefined, false);
|
||||
// if (this.props.searchFilter !== prevProps.searchFilter) {
|
||||
// this._checkSubListsOverflow();
|
||||
// }
|
||||
if (!this.props.searchFilter && prevProps.searchFilter) {
|
||||
this._layout = this._unfilteredlayout;
|
||||
forceLayoutUpdate = true;
|
||||
} else if (this.props.searchFilter && !prevProps.searchFilter) {
|
||||
this._layout = this._filteredLayout;
|
||||
forceLayoutUpdate = true;
|
||||
}
|
||||
this._layout.update(
|
||||
this._layoutSections,
|
||||
this.resizeContainer && this.resizeContainer.clientHeight,
|
||||
forceLayoutUpdate,
|
||||
);
|
||||
// TODO: call layout.setAvailableHeight, window height was changed when bannerShown prop was changed
|
||||
this._checkSubListsOverflow();
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
@ -617,7 +639,7 @@ module.exports = React.createClass({
|
||||
onHeaderClick(collapsed);
|
||||
}
|
||||
};
|
||||
const startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
|
||||
let startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
|
||||
this._layoutSections.push({
|
||||
id: chosenKey,
|
||||
count: len,
|
||||
@ -625,6 +647,7 @@ module.exports = React.createClass({
|
||||
let subList = (<RoomSubList
|
||||
ref={this._subListRef.bind(this, chosenKey)}
|
||||
startAsHidden={startAsHidden}
|
||||
forceExpand={!!this.props.searchFilter}
|
||||
onHeaderClick={onSubListHeaderClick}
|
||||
key={chosenKey}
|
||||
label={label}
|
||||
|
52
src/components/views/settings/tabs/FlairSettingsTab.js
Normal file
52
src/components/views/settings/tabs/FlairSettingsTab.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {_t} from "../../../../languageHandler";
|
||||
import {DragDropContext} from "react-beautiful-dnd";
|
||||
import GroupUserSettings from "../../groups/GroupUserSettings";
|
||||
import MatrixClientPeg from "../../../../MatrixClientPeg";
|
||||
import PropTypes from "prop-types";
|
||||
import {MatrixClient} from "matrix-js-sdk";
|
||||
|
||||
export default class FlairSettingsTab extends React.Component {
|
||||
static childContextTypes = {
|
||||
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
matrixClient: MatrixClientPeg.get(),
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="mx_SettingsTab">
|
||||
<span className="mx_SettingsTab_heading">{_t("Flair")}</span>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<DragDropContext>
|
||||
<GroupUserSettings />
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
|
||||
"The platform you're on": "The platform you're on",
|
||||
"The version of Riot.im": "The version of Riot.im",
|
||||
"Whether or not you're logged in (we don't record your user name)": "Whether or not you're logged in (we don't record your user name)",
|
||||
"Whether or not you're logged in (we don't record your username)": "Whether or not you're logged in (we don't record your username)",
|
||||
"Your language of choice": "Your language of choice",
|
||||
"Which officially provided instance you are using, if any": "Which officially provided instance you are using, if any",
|
||||
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
|
||||
@ -305,7 +305,7 @@
|
||||
"Uploading report": "Uploading report",
|
||||
"Waiting for response from server": "Waiting for response from server",
|
||||
"Messages containing my display name": "Messages containing my display name",
|
||||
"Messages containing my user name": "Messages containing my user name",
|
||||
"Messages containing my username": "Messages containing my username",
|
||||
"Messages containing @room": "Messages containing @room",
|
||||
"Messages in one-to-one chats": "Messages in one-to-one chats",
|
||||
"Encrypted messages in one-to-one chats": "Encrypted messages in one-to-one chats",
|
||||
@ -1170,9 +1170,8 @@
|
||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
|
||||
"Custom Server Options": "Custom Server Options",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.",
|
||||
"This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.",
|
||||
"You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.",
|
||||
"You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.",
|
||||
"To continue, please enter your password.": "To continue, please enter your password.",
|
||||
"Password:": "Password:",
|
||||
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
|
||||
@ -1184,25 +1183,34 @@
|
||||
"Please enter the code it contains:": "Please enter the code it contains:",
|
||||
"Code": "Code",
|
||||
"Start authentication": "Start authentication",
|
||||
"Your Modular server": "Your Modular server",
|
||||
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.",
|
||||
"Server Name": "Server Name",
|
||||
"The email field must not be blank.": "The email field must not be blank.",
|
||||
"The user name field must not be blank.": "The user name field must not be blank.",
|
||||
"The username field must not be blank.": "The username field must not be blank.",
|
||||
"The phone number field must not be blank.": "The phone number field must not be blank.",
|
||||
"The password field must not be blank.": "The password field must not be blank.",
|
||||
"Username on %(hs)s": "Username on %(hs)s",
|
||||
"User name": "User name",
|
||||
"Username": "Username",
|
||||
"Mobile phone number": "Mobile phone number",
|
||||
"Not sure of your password? <a>Set a new one</a>": "Not sure of your password? <a>Set a new one</a>",
|
||||
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
||||
"Your account": "Your account",
|
||||
"Your %(serverName)s account": "Your %(serverName)s account",
|
||||
"Sign in with": "Sign in with",
|
||||
"Sign in": "Sign in",
|
||||
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
||||
"Email address (optional)": "Email address (optional)",
|
||||
"Mobile phone number (optional)": "Mobile phone number (optional)",
|
||||
"Default server": "Default server",
|
||||
"Custom server": "Custom server",
|
||||
"Home server URL": "Home server URL",
|
||||
"Identity server URL": "Identity server URL",
|
||||
"What does this mean?": "What does this mean?",
|
||||
"Other servers": "Other servers",
|
||||
"Enter custom server URLs <a>What does this mean?</a>": "Enter custom server URLs <a>What does this mean?</a>",
|
||||
"Homeserver URL": "Homeserver URL",
|
||||
"Identity Server URL": "Identity Server URL",
|
||||
"Free": "Free",
|
||||
"Join millions for free on the largest public server": "Join millions for free on the largest public server",
|
||||
"Premium": "Premium",
|
||||
"Premium hosting for organisations <a>Learn more</a>": "Premium hosting for organisations <a>Learn more</a>",
|
||||
"Other": "Other",
|
||||
"Find other public servers or use a custom server": "Find other public servers or use a custom server",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
||||
"Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.",
|
||||
@ -1405,7 +1413,7 @@
|
||||
"This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
|
||||
"An email address is required to register on this homeserver.": "An email address is required to register on this homeserver.",
|
||||
"A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.",
|
||||
"You need to enter a user name.": "You need to enter a user name.",
|
||||
"You need to enter a username.": "You need to enter a username.",
|
||||
"An unknown error occurred.": "An unknown error occurred.",
|
||||
"Create your account": "Create your account",
|
||||
"Commands": "Commands",
|
||||
|
@ -83,7 +83,7 @@ module.exports = {
|
||||
// Messages containing user's username (localpart/MXID)
|
||||
".m.rule.contains_user_name": new VectorPushRuleDefinition({
|
||||
kind: "override",
|
||||
description: _td("Messages containing my user name"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||
description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
loud: StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND,
|
||||
|
@ -16,9 +16,6 @@ limitations under the License.
|
||||
|
||||
import FixedDistributor from "./fixed";
|
||||
|
||||
// const allowWhitespace = true;
|
||||
const handleHeight = 1;
|
||||
|
||||
function clamp(height, min, max) {
|
||||
if (height > max) return max;
|
||||
if (height < min) return min;
|
||||
@ -26,7 +23,7 @@ function clamp(height, min, max) {
|
||||
}
|
||||
|
||||
export class Layout {
|
||||
constructor(applyHeight, initialSizes, collapsedState) {
|
||||
constructor(applyHeight, initialSizes, collapsedState, options) {
|
||||
// callback to set height of section
|
||||
this._applyHeight = applyHeight;
|
||||
// list of {id, count} objects,
|
||||
@ -41,6 +38,17 @@ export class Layout {
|
||||
this._sectionHeights = Object.assign({}, initialSizes);
|
||||
// in-progress heights, while dragging. Committed on mouse-up.
|
||||
this._heights = [];
|
||||
// use while manually resizing to cancel
|
||||
// the resize for a given mouse position
|
||||
// when the previous resize made the layout
|
||||
// constrained
|
||||
this._clampedOffset = 0;
|
||||
// used while manually resizing, to clear
|
||||
// _clampedOffset when the direction of resizing changes
|
||||
this._lastOffset = 0;
|
||||
|
||||
this._allowWhitespace = options && options.allowWhitespace;
|
||||
this._handleHeight = (options && options.handleHeight) || 0;
|
||||
}
|
||||
|
||||
setAvailableHeight(newSize) {
|
||||
@ -60,7 +68,7 @@ export class Layout {
|
||||
this._applyNewSize();
|
||||
}
|
||||
|
||||
update(sections, availableHeight) {
|
||||
update(sections, availableHeight, force = false) {
|
||||
let heightChanged = false;
|
||||
|
||||
if (Number.isFinite(availableHeight) && availableHeight !== this._availableHeight) {
|
||||
@ -75,7 +83,7 @@ export class Layout {
|
||||
return a.id !== b.id || a.count !== b.count;
|
||||
});
|
||||
|
||||
if (!heightChanged && !sectionsChanged) {
|
||||
if (!heightChanged && !sectionsChanged && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -104,7 +112,7 @@ export class Layout {
|
||||
const collapsed = this._collapsedState[section.id];
|
||||
return count + (collapsed ? 0 : 1);
|
||||
}, 0);
|
||||
return this._availableHeight - ((nonCollapsedSectionCount - 1) * handleHeight);
|
||||
return this._availableHeight - ((nonCollapsedSectionCount - 1) * this._handleHeight);
|
||||
}
|
||||
|
||||
_applyNewSize() {
|
||||
@ -130,9 +138,10 @@ export class Layout {
|
||||
|
||||
if (collapsed) {
|
||||
return this._sectionHeight(0);
|
||||
} else if (!this._allowWhitespace) {
|
||||
return this._sectionHeight(section.count);
|
||||
} else {
|
||||
return 100000;
|
||||
// return this._sectionHeight(section.count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,6 +277,22 @@ export class Layout {
|
||||
this._sectionHeights[section.id] = this._heights[i];
|
||||
});
|
||||
}
|
||||
|
||||
_setUncommittedSectionHeight(sectionIndex, offset) {
|
||||
if (Math.sign(offset) != Math.sign(this._lastOffset)) {
|
||||
this._clampedOffset = undefined;
|
||||
}
|
||||
if (this._clampedOffset !== undefined) {
|
||||
if (offset < 0 && offset < this._clampedOffset) {
|
||||
return;
|
||||
}
|
||||
if (offset > 0 && offset > this._clampedOffset) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._clampedOffset = this._relayout(sectionIndex, offset);
|
||||
this._lastOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
class Handle {
|
||||
@ -278,7 +303,10 @@ class Handle {
|
||||
}
|
||||
|
||||
setHeight(height) {
|
||||
this._layout._relayout(this._sectionIndex, height - this._initialHeight);
|
||||
this._layout._setUncommittedSectionHeight(
|
||||
this._sectionIndex,
|
||||
height - this._initialHeight,
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user