diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..afc29f0142 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +patreon: matrixdotorg +liberapay: matrixdotorg diff --git a/res/css/_components.scss b/res/css/_components.scss index 2a91f08ee4..843f314bd1 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -50,7 +50,6 @@ @import "./views/context_menus/_TopLeftMenu.scss"; @import "./views/dialogs/_AddressPickerDialog.scss"; @import "./views/dialogs/_Analytics.scss"; -@import "./views/dialogs/_BugReportDialog.scss"; @import "./views/dialogs/_ChangelogDialog.scss"; @import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss"; @import "./views/dialogs/_ConfirmUserActionDialog.scss"; diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index fc1538a13d..b6644c1752 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -39,7 +39,11 @@ limitations under the License. z-index: 5001; } -.mx_ContextualMenu.mx_ContextualMenu_right { +.mx_ContextualMenu_right { + right: 0; +} + +.mx_ContextualMenu.mx_ContextualMenu_withChevron_right { right: 8px; } @@ -66,7 +70,11 @@ limitations under the License. right: 1px; } -.mx_ContextualMenu.mx_ContextualMenu_left { +.mx_ContextualMenu_left { + left: 0; +} + +.mx_ContextualMenu.mx_ContextualMenu_withChevron_left { left: 8px; } @@ -93,7 +101,11 @@ limitations under the License. left: 1px; } -.mx_ContextualMenu.mx_ContextualMenu_top { +.mx_ContextualMenu_top { + top: 0; +} + +.mx_ContextualMenu.mx_ContextualMenu_withChevron_top { top: 8px; } @@ -120,7 +132,11 @@ limitations under the License. top: 1px; } -.mx_ContextualMenu.mx_ContextualMenu_bottom { +.mx_ContextualMenu_bottom { + bottom: 0; +} + +.mx_ContextualMenu.mx_ContextualMenu_withChevron_bottom { bottom: 8px; } diff --git a/res/css/structures/_GenericErrorPage.scss b/res/css/structures/_GenericErrorPage.scss index 44ea73444e..2b9e9f5e7d 100644 --- a/res/css/structures/_GenericErrorPage.scss +++ b/res/css/structures/_GenericErrorPage.scss @@ -12,7 +12,7 @@ right: 0; margin: auto; width: 500px; - height: 200px; + height: 125px; border: 1px solid #f22; padding: 10px 10px 20px; background-color: #fcc; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 4eff5c33e4..9bcd79a357 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -62,6 +62,15 @@ limitations under the License. margin-bottom: 12px; } +.mx_Login_error.mx_Login_serverError { + text-align: left; + font-weight: normal; +} + +.mx_Login_error.mx_Login_serverError.mx_Login_serverErrorNonFatal { + color: $orange-warning-color; +} + .mx_Login_type_container { display: flex; align-items: center; diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 16ac876869..cce3b5dbf5 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -72,7 +72,6 @@ limitations under the License. } .mx_Field input { - width: 100%; box-sizing: border-box; } @@ -110,7 +109,6 @@ limitations under the License. .mx_AuthBody_fieldRow > .mx_Field { margin: 0 5px; - flex: 1; } .mx_AuthBody_fieldRow > .mx_Field:first-child { diff --git a/res/css/views/auth/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss index fe96da2019..a31feb75d7 100644 --- a/res/css/views/auth/_ServerConfig.scss +++ b/res/css/views/auth/_ServerConfig.scss @@ -20,7 +20,6 @@ limitations under the License. } .mx_ServerConfig_fields .mx_Field { - flex: 1; margin: 0 5px; } diff --git a/res/css/views/dialogs/_BugReportDialog.scss b/res/css/views/dialogs/_BugReportDialog.scss deleted file mode 100644 index 90ef55b945..0000000000 --- a/res/css/views/dialogs/_BugReportDialog.scss +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2017 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_BugReportDialog .mx_Field { - flex: 1; -} - -.mx_BugReportDialog_field_input { - // TODO: We should really apply this to all .mx_Field inputs. - // See https://github.com/vector-im/riot-web/issues/9344. - flex: 1; -} diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 1f5d36b57a..8e669acd10 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -23,7 +23,11 @@ limitations under the License. cursor: default !important; } -.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button, .mx_DevTools_RoomStateExplorer_query { +.mx_DevTools_RoomStateExplorer_query { + margin-bottom: 10px; +} + +.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button { margin-bottom: 10px; width: 100%; } @@ -75,7 +79,6 @@ limitations under the License. max-width: 684px; min-height: 250px; padding: 10px; - width: 100%; } .mx_DevTools_content .mx_Field_input { diff --git a/res/css/views/dialogs/_SetPasswordDialog.scss b/res/css/views/dialogs/_SetPasswordDialog.scss index 28a8b7c9d7..325ff6c6ed 100644 --- a/res/css/views/dialogs/_SetPasswordDialog.scss +++ b/res/css/views/dialogs/_SetPasswordDialog.scss @@ -21,7 +21,6 @@ limitations under the License. color: $primary-fg-color; background-color: $primary-bg-color; font-size: 15px; - width: 100%; max-width: 280px; margin-bottom: 10px; } diff --git a/res/css/views/elements/_EditableItemList.scss b/res/css/views/elements/_EditableItemList.scss index be96d811d3..51fa4c4423 100644 --- a/res/css/views/elements/_EditableItemList.scss +++ b/res/css/views/elements/_EditableItemList.scss @@ -42,12 +42,6 @@ limitations under the License. margin-right: 5px; } -.mx_EditableItemList_newItem .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); -} - .mx_EditableItemList_label { margin-bottom: 5px; -} \ No newline at end of file +} diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 147bb3b471..f9cbf8c541 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -42,6 +42,7 @@ limitations under the License. padding: 8px 9px; color: $primary-fg-color; background-color: $primary-bg-color; + flex: 1; } .mx_Field select { diff --git a/res/css/views/elements/_PowerSelector.scss b/res/css/views/elements/_PowerSelector.scss index 69f3a8eebb..799f6f246e 100644 --- a/res/css/views/elements/_PowerSelector.scss +++ b/res/css/views/elements/_PowerSelector.scss @@ -20,6 +20,5 @@ limitations under the License. .mx_PowerSelector .mx_Field select, .mx_PowerSelector .mx_Field input { - width: 100%; box-sizing: border-box; } diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 749cfeebe6..685c2bb018 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -26,6 +26,8 @@ limitations under the License. top: -18px; right: 8px; user-select: none; + // Ensure the action bar appears above over things, like the read marker. + z-index: 1; > * { display: inline-block; diff --git a/res/css/views/messages/_ReactionsRow.scss b/res/css/views/messages/_ReactionsRow.scss index fb66ffbb8c..3b764e97b4 100644 --- a/res/css/views/messages/_ReactionsRow.scss +++ b/res/css/views/messages/_ReactionsRow.scss @@ -16,4 +16,5 @@ limitations under the License. .mx_ReactionsRow { margin: 6px 0; + color: $primary-fg-color; } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 7d8a58f7a8..a6194832a3 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -56,13 +56,17 @@ limitations under the License. color: $primary-fg-color; font-size: 14px; display: inline-block; /* anti-zalgo, with overflow hidden */ - overflow-y: hidden; + overflow: hidden; cursor: pointer; padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; line-height: 17px; + /* the next three lines, along with overflow hidden, truncate long display names */ + white-space: nowrap; + text-overflow: ellipsis; + max-width: calc(100% - 65px); } .mx_EventTile .mx_SenderProfile .mx_Flair { @@ -243,6 +247,7 @@ limitations under the License. height: 14px; top: 29px; user-select: none; + z-index: 1; } .mx_EventTile_continuation .mx_EventTile_readAvatars, diff --git a/res/css/views/rooms/_MemberInfo.scss b/res/css/views/rooms/_MemberInfo.scss index c3b3ca2f7d..bb38c41581 100644 --- a/res/css/views/rooms/_MemberInfo.scss +++ b/res/css/views/rooms/_MemberInfo.scss @@ -43,6 +43,8 @@ limitations under the License. .mx_MemberInfo_name h2 { flex: 1; + overflow-x: auto; + max-height: 50px; } .mx_MemberInfo h2 { diff --git a/res/css/views/settings/_EmailAddresses.scss b/res/css/views/settings/_EmailAddresses.scss index eef804a33b..4f9541af2c 100644 --- a/res/css/views/settings/_EmailAddresses.scss +++ b/res/css/views/settings/_EmailAddresses.scss @@ -35,9 +35,3 @@ limitations under the License. .mx_ExistingEmailAddress_confirmBtn { margin-right: 5px; } - -.mx_EmailAddresses_new .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); -} diff --git a/res/css/views/settings/_PhoneNumbers.scss b/res/css/views/settings/_PhoneNumbers.scss index 2f54babd6f..a3891882c2 100644 --- a/res/css/views/settings/_PhoneNumbers.scss +++ b/res/css/views/settings/_PhoneNumbers.scss @@ -36,12 +36,6 @@ limitations under the License. margin-right: 5px; } -.mx_PhoneNumbers_new .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); -} - .mx_PhoneNumbers_input { display: flex; align-items: center; diff --git a/res/css/views/settings/_ProfileSettings.scss b/res/css/views/settings/_ProfileSettings.scss index b2e449ac34..a972162618 100644 --- a/res/css/views/settings/_ProfileSettings.scss +++ b/res/css/views/settings/_ProfileSettings.scss @@ -22,11 +22,6 @@ limitations under the License. flex-grow: 1; } -.mx_ProfileSettings_controls .mx_Field #profileDisplayName, -.mx_ProfileSettings_controls .mx_Field #profileTopic { - width: calc(100% - 20px); // subtract 10px padding on left and right -} - .mx_ProfileSettings_controls .mx_Field #profileTopic { height: 4em; } diff --git a/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss index 91d7ed2c7d..af55820d66 100644 --- a/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss @@ -17,7 +17,3 @@ limitations under the License. .mx_GeneralRoomSettingsTab_profileSection { margin-top: 10px; } - -.mx_GeneralRoomSettingsTab .mx_AliasSettings .mx_Field select { - width: 100%; -} diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index bec013674a..091c98ffb8 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -14,33 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GeneralUserSettingsTab_changePassword, -.mx_GeneralUserSettingsTab_themeSection { - display: block; -} - .mx_GeneralUserSettingsTab_changePassword .mx_Field, .mx_GeneralUserSettingsTab_themeSection .mx_Field { - display: block; margin-right: 100px; // Align with the other fields on the page } -.mx_GeneralUserSettingsTab_changePassword .mx_Field input { - display: block; - width: calc(100% - 20px); // subtract 10px padding on left and right -} - .mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child { margin-top: 0; } -.mx_GeneralUserSettingsTab_themeSection .mx_Field select { - display: block; - width: 100%; -} - .mx_GeneralUserSettingsTab_accountSection > .mx_EmailAddresses, .mx_GeneralUserSettingsTab_accountSection > .mx_PhoneNumbers, .mx_GeneralUserSettingsTab_languageInput { margin-right: 100px; // Align with the other fields on the page -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss index f447221b7a..b3430f47af 100644 --- a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss @@ -17,11 +17,3 @@ limitations under the License. .mx_PreferencesUserSettingsTab .mx_Field { margin-right: 100px; // Align with the rest of the controls } - -.mx_PreferencesUserSettingsTab .mx_Field input { - display: block; - - // Subtract 10px padding on left and right - // This is to keep the input aligned with the rest of the tab's controls. - width: calc(100% - 20px); -} diff --git a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss index f5dba9831e..36c8cfd896 100644 --- a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss @@ -14,11 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_VoiceUserSettingsTab .mx_Field select { - width: 100%; - max-width: 100%; -} - .mx_VoiceUserSettingsTab .mx_Field { margin-right: 100px; // align with the rest of the fields } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 712d905b43..8244485ee3 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -35,8 +35,9 @@ $selection-fg-color: $primary-bg-color; $focus-brightness: 105%; -// red warning colour -$warning-color: $notice-primary-color; +// warning colours +$warning-color: $notice-primary-color; // red +$orange-warning-color: #ff8d13; // used for true warnings // background colour for warnings $warning-bg-color: #DF2A8B; $info-bg-color: #2A9EDF; diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index a8bbce10dc..6cd06678ea 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -51,6 +51,7 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); const WHITESPACE_REGEX = new RegExp("\\s", "g"); const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})+$`, 'i'); +const SINGLE_EMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})$`, 'i'); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; @@ -63,10 +64,19 @@ const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet']; * need emojification. * unicodeToImage uses this function. */ -export function containsEmoji(str) { +export function mightContainEmoji(str) { return SURROGATE_PAIR_PATTERN.test(str) || SYMBOL_PATTERN.test(str); } +/** + * Returns true if the string definitely contains a single emoji. + * @param {String} str String to test + * @return {Boolean} + */ +export function isSingleEmoji(str) { + return mightContainEmoji(str) && SINGLE_EMOJI_REGEX.test(str); +} + /** * Returns the shortcode for an emoji character. * @@ -428,7 +438,7 @@ export function bodyToHtml(content, highlights, opts={}) { if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody); strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(content.body) : content.body; - bodyHasEmoji = containsEmoji(isHtmlMessage ? formattedBody : content.body); + bodyHasEmoji = mightContainEmoji(isHtmlMessage ? formattedBody : content.body); // Only generate safeBody if the message was sent as org.matrix.custom.html if (isHtmlMessage) { diff --git a/src/Keyboard.js b/src/Keyboard.js index bf83a1a05f..fb7d692ce3 100644 --- a/src/Keyboard.js +++ b/src/Keyboard.js @@ -58,6 +58,7 @@ export const KeyCode = { KEY_X: 88, KEY_Y: 89, KEY_Z: 90, + KEY_BACKTICK: 223, }; export function isOnlyCtrlOrCmdKeyEvent(ev) { diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 7c78b71f2b..195377fbe9 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -233,7 +233,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { guest: true, }, true).then(() => true); }, (err) => { - console.error("Failed to register as guest: " + err + " " + err.data); + console.error("Failed to register as guest", err); return false; }); } diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 574f05bf85..07499a3a87 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -51,6 +51,7 @@ interface MatrixClientCreds { class MatrixClientPeg { constructor() { this.matrixClient = null; + this._justRegisteredUserId = null; // These are the default options used when when the // client is started in 'start'. These can be altered @@ -85,6 +86,31 @@ class MatrixClientPeg { MatrixActionCreators.stop(); } + /* + * If we've registered a user ID we set this to the ID of the + * user we've just registered. If they then go & log in, we + * can send them to the welcome user (obviously this doesn't + * guarentee they'll get a chat with the welcome user). + * + * @param {string} uid The user ID of the user we've just registered + */ + setJustRegisteredUserId(uid) { + this._justRegisteredUserId = uid; + } + + /* + * Returns true if the current user has just been registered by this + * client as determined by setJustRegisteredUserId() + * + * @returns {bool} True if user has just been registered + */ + currentUserIsJustRegistered() { + return ( + this.matrixClient && + this.matrixClient.credentials.userId === this._justRegisteredUserId + ); + } + /** * Replace this MatrixClientPeg's client with a client instance that has * homeserver / identity server URLs and active credentials diff --git a/src/components/structures/ContextualMenu.js b/src/components/structures/ContextualMenu.js index 345eae2b18..edd6f79270 100644 --- a/src/components/structures/ContextualMenu.js +++ b/src/components/structures/ContextualMenu.js @@ -183,11 +183,14 @@ export default class ContextualMenu extends React.Component { const menuClasses = classNames({ 'mx_ContextualMenu': true, - 'mx_ContextualMenu_noChevron': chevronFace === 'none', - 'mx_ContextualMenu_left': chevronFace === 'left', - 'mx_ContextualMenu_right': chevronFace === 'right', - 'mx_ContextualMenu_top': chevronFace === 'top', - 'mx_ContextualMenu_bottom': chevronFace === 'bottom', + 'mx_ContextualMenu_left': !hasChevron && position.left, + 'mx_ContextualMenu_right': !hasChevron && position.right, + 'mx_ContextualMenu_top': !hasChevron && position.top, + 'mx_ContextualMenu_bottom': !hasChevron && position.bottom, + 'mx_ContextualMenu_withChevron_left': chevronFace === 'left', + 'mx_ContextualMenu_withChevron_right': chevronFace === 'right', + 'mx_ContextualMenu_withChevron_top': chevronFace === 'top', + 'mx_ContextualMenu_withChevron_bottom': chevronFace === 'bottom', }); const menuStyle = {}; diff --git a/src/components/structures/EmbeddedPage.js b/src/components/structures/EmbeddedPage.js index e3c197e1db..ecc01a443d 100644 --- a/src/components/structures/EmbeddedPage.js +++ b/src/components/structures/EmbeddedPage.js @@ -46,6 +46,8 @@ export default class EmbeddedPage extends React.PureComponent { constructor(props) { super(props); + this._dispatcherRef = null; + this.state = { page: '', }; @@ -90,7 +92,7 @@ export default class EmbeddedPage extends React.PureComponent { componentWillUnmount() { this._unmounted = true; - dis.unregister(this._dispatcherRef); + if (this._dispatcherRef !== null) dis.unregister(this._dispatcherRef); } onAction = (payload) => { diff --git a/src/components/structures/GenericErrorPage.js b/src/components/structures/GenericErrorPage.js index 3d8e68cea7..ab7d4f9311 100644 --- a/src/components/structures/GenericErrorPage.js +++ b/src/components/structures/GenericErrorPage.js @@ -16,22 +16,18 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import {_t} from "../../languageHandler"; export default class GenericErrorPage extends React.PureComponent { static propTypes = { + title: PropTypes.object.isRequired, // jsx for title message: PropTypes.object.isRequired, // jsx to display }; render() { return
{this.props.message}
-{_t( - "If this is unexpected, please contact your system administrator " + - "or technical support representative.", - )}