From ce24165e19a1b61f1447306b68d35c569d72de2f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 8 Feb 2019 16:44:03 +0000 Subject: [PATCH 001/273] port over low_bandwidth mode to develop --- src/Lifecycle.js | 5 ++++- src/MatrixClientPeg.js | 2 +- src/components/structures/TimelinePanel.js | 2 ++ src/components/structures/UserSettings.js | 9 ++++++++- src/components/views/avatars/BaseAvatar.js | 11 ++++++++--- src/components/views/rooms/MessageComposerInput.js | 1 + src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 5 +++++ 8 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 54ac605c65..2ea8aa190a 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -31,6 +31,7 @@ import Modal from './Modal'; import sdk from './index'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import PlatformPeg from "./PlatformPeg"; +import SettingsStore from "./settings/SettingsStore"; import {sendLoginRequest} from "./Login"; /** @@ -440,7 +441,9 @@ async function startMatrixClient() { Notifier.start(); UserActivity.start(); - Presence.start(); + if (!SettingsStore.getValue("lowBandwidth")) { + Presence.start(); + } DMRoomMap.makeShared().start(); ActiveWidgetStore.start(); diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 882a913452..12301e3716 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -115,7 +115,7 @@ class MatrixClientPeg { // try to initialise e2e on the new client try { // check that we have a version of the js-sdk which includes initCrypto - if (this.matrixClient.initCrypto) { + if (!SettingsStore.getValue("lowBandwidth") && this.matrixClient.initCrypto) { await this.matrixClient.initCrypto(); } } catch (e) { diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 9fe83c2c2d..3bdcf542b1 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -584,6 +584,8 @@ var TimelinePanel = React.createClass({ }, sendReadReceipt: function() { + if (SettingsStore.getValue("lowBandwidth")) return; + if (!this.refs.messagePanel) return; if (!this.props.manageReadReceipts) return; // This happens on user_activity_end which is delayed, and it's diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 809b06c3d6..0aa564cc85 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -84,6 +84,13 @@ const SIMPLE_SETTINGS = [ { id: "enableWidgetScreenshots" }, { id: "pinMentionedRooms" }, { id: "pinUnreadRooms" }, + { + id: "lowBandwidth", + fn: () => { + PlatformPeg.get().reload(); + }, + level: SettingLevel.DEVICE, + }, { id: "showDeveloperTools" }, { id: "promptBeforeInviteUnknownUsers" }, ]; @@ -644,7 +651,7 @@ module.exports = React.createClass({
); diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 47de7c9dc4..7241e7cd4c 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -20,6 +20,7 @@ import PropTypes from 'prop-types'; import { MatrixClient } from 'matrix-js-sdk'; import AvatarLogic from '../../../Avatar'; import sdk from '../../../index'; +import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; module.exports = React.createClass({ @@ -104,9 +105,13 @@ module.exports = React.createClass({ // work out the full set of urls to try to load. This is formed like so: // imageUrls: [ props.url, props.urls, default image ] - const urls = props.urls || []; - if (props.url) { - urls.unshift(props.url); // put in urls[0] + let urls = []; + if (!SettingsStore.getValue("lowBandwidth")) { + urls = props.urls || []; + + if (props.url) { + urls.unshift(props.url); // put in urls[0] + } } let defaultImageUrl = null; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index ab89e402ae..0e9442edb9 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -487,6 +487,7 @@ export default class MessageComposerInput extends React.Component { sendTyping(isTyping) { if (!SettingsStore.getValue('sendTypingNotifications')) return; + if (SettingsStore.getValue('lowBandwidth')) return; MatrixClientPeg.get().sendTyping( this.props.room.roomId, this.isTyping, TYPING_SERVER_TIMEOUT, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 623dd92613..e29be2da45 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -300,6 +300,7 @@ "Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets", "Prompt before sending invites to potentially invalid matrix IDs": "Prompt before sending invites to potentially invalid matrix IDs", "Show developer tools": "Show developer tools", + "Low Bandwidth Mode": "Low Bandwidth Mode", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index f21e94ea4a..177a90ef5d 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -362,4 +362,9 @@ export const SETTINGS = { displayName: _td('Show developer tools'), default: false, }, + "lowBandwidth": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, + displayName: _td('Low Bandwidth Mode'), + default: false, + }, }; From 636cb8a5ccb00e91df624386af66c2c35f310c31 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 22:57:49 -0600 Subject: [PATCH 002/273] Have ServerConfig and co. do validation of the config in-house This also causes the components to produce a ValidatedServerConfig for use by other components. --- res/css/views/auth/_ServerConfig.scss | 5 + .../views/auth/ModularServerConfig.js | 92 ++++++++++----- src/components/views/auth/ServerConfig.js | 111 +++++++++++------- src/utils/AutoDiscoveryUtils.js | 104 ++++++++++++++++ 4 files changed, 238 insertions(+), 74 deletions(-) create mode 100644 src/utils/AutoDiscoveryUtils.js diff --git a/res/css/views/auth/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss index 79ad9e8238..fe96da2019 100644 --- a/res/css/views/auth/_ServerConfig.scss +++ b/res/css/views/auth/_ServerConfig.scss @@ -35,3 +35,8 @@ limitations under the License. .mx_ServerConfig_help:link { opacity: 0.8; } + +.mx_ServerConfig_error { + display: block; + color: $warning-color; +} diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index 9c6c4b01bf..ea22577dbd 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -18,9 +18,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; +import SdkConfig from "../../../SdkConfig"; +import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; +import * as ServerType from '../../views/auth/ServerTypeSelector'; const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; +// TODO: TravisR - Can this extend ServerConfig for most things? + /* * Configure the Modular server name. * @@ -31,65 +37,87 @@ 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, + // The current configuration that the user is expecting to change. + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, 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, + busy: false, + errorText: "", + hsUrl: props.serverConfig.hsUrl, + isUrl: props.serverConfig.isUrl, }; } componentWillReceiveProps(newProps) { - if (newProps.customHsUrl === this.state.hsUrl) return; + if (newProps.serverConfig.hsUrl === this.state.hsUrl && + newProps.serverConfig.isUrl === this.state.isUrl) return; + + this.validateAndApplyServer(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); + } + + async validateAndApplyServer(hsUrl, isUrl) { + // Always try and use the defaults first + const defaultConfig: ValidatedServerConfig = SdkConfig.get()["validated_server_config"]; + if (defaultConfig.hsUrl === hsUrl && defaultConfig.isUrl === isUrl) { + this.setState({busy: false, errorText: ""}); + this.props.onServerConfigChange(defaultConfig); + return defaultConfig; + } this.setState({ - hsUrl: newProps.customHsUrl, - }); - this.props.onServerConfigChange({ - hsUrl: newProps.customHsUrl, - isUrl: this.props.defaultIsUrl, + hsUrl, + isUrl, + busy: true, + errorText: "", }); + + try { + const result = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl); + this.setState({busy: false, errorText: ""}); + this.props.onServerConfigChange(result); + return result; + } catch (e) { + console.error(e); + let message = _t("Unable to validate homeserver/identity server"); + if (e.translatedMessage) { + message = e.translatedMessage; + } + this.setState({ + busy: false, + errorText: message, + }); + } + } + + async validateServer() { + // TODO: Do we want to support .well-known lookups here? + // If for some reason someone enters "matrix.org" for a URL, we could do a lookup to + // find their homeserver without demanding they use "https://matrix.org" + return this.validateAndApplyServer(this.state.hsUrl, ServerType.TYPES.PREMIUM.identityServerUrl); } onHomeserverBlur = (ev) => { this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => { - this.props.onServerConfigChange({ - hsUrl: this.state.hsUrl, - isUrl: this.props.defaultIsUrl, - }); + this.validateServer(); }); - } + }; onHomeserverChange = (ev) => { const hsUrl = ev.target.value; this.setState({ hsUrl }); - } + }; _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { @@ -116,7 +144,7 @@ export default class ModularServerConfig extends React.PureComponent {
{ this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => { - this.props.onServerConfigChange({ - hsUrl: this.state.hsUrl, - isUrl: this.state.isUrl, - }); + this.validateServer(); }); - } + }; onHomeserverChange = (ev) => { const hsUrl = ev.target.value; this.setState({ hsUrl }); - } + }; onIdentityServerBlur = (ev) => { this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => { - this.props.onServerConfigChange({ - hsUrl: this.state.hsUrl, - isUrl: this.state.isUrl, - }); + this.validateServer(); }); - } + }; onIdentityServerChange = (ev) => { const isUrl = ev.target.value; this.setState({ isUrl }); - } + }; _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { @@ -114,11 +134,15 @@ export default class ServerConfig extends React.PureComponent { showHelpPopup = () => { const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog'); Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog); - } + }; render() { const Field = sdk.getComponent('elements.Field'); + const errorText = this.state.errorText + ? {this.state.errorText} + : null; + return (

{_t("Other servers")}

@@ -127,20 +151,23 @@ export default class ServerConfig extends React.PureComponent { { sub } , })} + {errorText}
diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js new file mode 100644 index 0000000000..318c706136 --- /dev/null +++ b/src/utils/AutoDiscoveryUtils.js @@ -0,0 +1,104 @@ +/* +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 {AutoDiscovery} from "matrix-js-sdk"; +import {_td, newTranslatableError} from "../languageHandler"; +import {makeType} from "./TypeUtils"; +import SdkConfig from "../SdkConfig"; + +export class ValidatedServerConfig { + hsUrl: string; + hsName: string; + hsNameIsDifferent: string; + + isUrl: string; + identityEnabled: boolean; +} + +export default class AutoDiscoveryUtils { + static async validateServerConfigWithStaticUrls(homeserverUrl: string, identityUrl: string): ValidatedServerConfig { + if (!homeserverUrl) { + throw newTranslatableError(_td("No homeserver URL provided")); + } + + const wellknownConfig = { + "m.homeserver": { + base_url: homeserverUrl, + }, + "m.identity_server": { + base_url: identityUrl, + }, + }; + + const result = await AutoDiscovery.fromDiscoveryConfig(wellknownConfig); + + const url = new URL(homeserverUrl); + const serverName = url.hostname; + + return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result); + } + + static async validateServerName(serverName: string): ValidatedServerConfig { + const result = await AutoDiscovery.findClientConfig(serverName); + return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result); + } + + static buildValidatedConfigFromDiscovery(serverName: string, discoveryResult): ValidatedServerConfig { + if (!discoveryResult || !discoveryResult["m.homeserver"]) { + // This shouldn't happen without major misconfiguration, so we'll log a bit of information + // in the log so we can find this bit of codee but otherwise tell teh user "it broke". + console.error("Ended up in a state of not knowing which homeserver to connect to."); + throw newTranslatableError(_td("Unexpected error resolving homeserver configuration")); + } + + const hsResult = discoveryResult['m.homeserver']; + if (hsResult.state !== AutoDiscovery.SUCCESS) { + if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) { + throw newTranslatableError(hsResult.error); + } + throw newTranslatableError(_td("Unexpected error resolving homeserver configuration")); + } + + const isResult = discoveryResult['m.identity_server']; + let preferredIdentityUrl = "https://vector.im"; + if (isResult && isResult.state === AutoDiscovery.SUCCESS) { + preferredIdentityUrl = isResult["base_url"]; + } else if (isResult && isResult.state !== AutoDiscovery.PROMPT) { + console.error("Error determining preferred identity server URL:", isResult); + throw newTranslatableError(_td("Unexpected error resolving homeserver configuration")); + } + + const preferredHomeserverUrl = hsResult["base_url"]; + let preferredHomeserverName = serverName ? serverName : hsResult["server_name"]; + + const url = new URL(preferredHomeserverUrl); + if (!preferredHomeserverName) preferredHomeserverName = url.hostname; + + // It should have been set by now, so check it + if (!preferredHomeserverName) { + console.error("Failed to parse homeserver name from homeserver URL"); + throw newTranslatableError(_td("Unexpected error resolving homeserver configuration")); + } + + return makeType(ValidatedServerConfig, { + hsUrl: preferredHomeserverUrl, + hsName: preferredHomeserverName, + hsNameIsDifferent: url.hostname !== preferredHomeserverName, + isUrl: preferredIdentityUrl, + identityEnabled: !SdkConfig.get()['disable_identity_server'], + }); + } +} From 6b45e6031454dc74929a76d6c071c0200d65925a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:02:01 -0600 Subject: [PATCH 003/273] Update ServerTypeSelector for registration to use a server config --- .../structures/auth/Registration.js | 10 +------ src/components/views/auth/RegistrationForm.js | 27 ++++++----------- .../views/auth/ServerTypeSelector.js | 21 +++++++++---- src/utils/TypeUtils.js | 30 +++++++++++++++++++ 4 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 src/utils/TypeUtils.js diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 708118bb22..c579b2082d 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -446,13 +446,6 @@ module.exports = React.createClass({ onEditServerDetailsClick = this.onEditServerDetailsClick; } - // If the current HS URL is the default HS URL, then we can label it - // with the default HS name (if it exists). - let hsName; - if (this.state.hsUrl === this.props.defaultHsUrl) { - hsName = this.props.defaultServerName; - } - return ; } }, diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 6e55581af0..f815ad081d 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -26,6 +26,7 @@ import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; import { SAFE_LOCALPART_REGEX } from '../../../Registration'; import withValidation from '../elements/Validation'; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; const FIELD_EMAIL = 'field_email'; const FIELD_PHONE_NUMBER = 'field_phone_number'; @@ -51,11 +52,7 @@ module.exports = React.createClass({ onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise onEditServerDetailsClick: PropTypes.func, flows: PropTypes.arrayOf(PropTypes.object).isRequired, - // This is optional and only set if we used a server name to determine - // the HS URL via `.well-known` discovery. The server name is used - // instead of the HS URL when talking about "your account". - hsName: PropTypes.string, - hsUrl: PropTypes.string, + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, }, getDefaultProps: function() { @@ -499,20 +496,14 @@ module.exports = React.createClass({ }, render: function() { - let yourMatrixAccountText = _t('Create your Matrix account'); - if (this.props.hsName) { - yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', { - serverName: this.props.hsName, + let yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', { + serverName: this.props.serverConfig.hsName, + }); + if (this.props.serverConfig.hsNameIsDifferent) { + // TODO: TravisR - Use tooltip to underline + yourMatrixAccountText = _t('Create your Matrix account on ', {}, { + 'underlinedServerName': () => {this.props.serverConfig.hsName}, }); - } else { - try { - const parsedHsUrl = new URL(this.props.hsUrl); - yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', { - serverName: parsedHsUrl.hostname, - }); - } catch (e) { - // ignore - } } let editLink = null; diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js index 71d13da421..602de72f3f 100644 --- a/src/components/views/auth/ServerTypeSelector.js +++ b/src/components/views/auth/ServerTypeSelector.js @@ -19,6 +19,8 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import sdk from '../../../index'; import classnames from 'classnames'; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; +import {makeType} from "../../../utils/TypeUtils"; const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; @@ -32,8 +34,13 @@ export const TYPES = { label: () => _t('Free'), logo: () => , description: () => _t('Join millions for free on the largest public server'), - hsUrl: 'https://matrix.org', - isUrl: 'https://vector.im', + serverConfig: makeType(ValidatedServerConfig, { + hsUrl: "https://matrix.org", + hsName: "matrix.org", + hsNameIsDifferent: false, + isUrl: "https://vector.im", + identityEnabled: true, + }), }, PREMIUM: { id: PREMIUM, @@ -44,6 +51,7 @@ export const TYPES = { {sub} , }), + identityServerUrl: "https://vector.im", }, ADVANCED: { id: ADVANCED, @@ -56,10 +64,11 @@ export const TYPES = { }, }; -export function getTypeFromHsUrl(hsUrl) { +export function getTypeFromServerConfig(config) { + const {hsUrl} = config; if (!hsUrl) { return null; - } else if (hsUrl === TYPES.FREE.hsUrl) { + } else if (hsUrl === TYPES.FREE.serverConfig.hsUrl) { return FREE; } else if (new URL(hsUrl).hostname.endsWith('.modular.im')) { // This is an unlikely case to reach, as Modular defaults to hiding the @@ -76,7 +85,7 @@ export default class ServerTypeSelector extends React.PureComponent { selected: PropTypes.string, // Handler called when the selected type changes. onChange: PropTypes.func.isRequired, - } + }; constructor(props) { super(props); @@ -106,7 +115,7 @@ export default class ServerTypeSelector extends React.PureComponent { e.stopPropagation(); const type = e.currentTarget.dataset.id; this.updateSelectedType(type); - } + }; render() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); diff --git a/src/utils/TypeUtils.js b/src/utils/TypeUtils.js new file mode 100644 index 0000000000..abdd0eb2a0 --- /dev/null +++ b/src/utils/TypeUtils.js @@ -0,0 +1,30 @@ +/* +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. +*/ + +/** + * Creates a class of a given type using the objects defined. This + * is a stopgap function while we don't have TypeScript interfaces. + * In future, we'd define the `type` as an interface and just cast + * it instead of cheating like we are here. + * @param {Type} Type The type of class to construct. + * @param {*} opts The options (properties) to set on the object. + * @returns {*} The created object. + */ +export function makeType(Type: any, opts: any) { + const c = new Type(); + Object.assign(c, opts); + return c; +} From 00ebb5e1fd1c5246e99d44400939960d23b6b70c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:04:06 -0600 Subject: [PATCH 004/273] Make registration work with server configs The general idea is that we throw the object around between components so they can pull off the details they care about. --- .../structures/auth/Registration.js | 103 ++++++++---------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index c579b2082d..faab8190bd 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -17,16 +17,15 @@ limitations under the License. */ import Matrix from 'matrix-js-sdk'; - import Promise from 'bluebird'; import React from 'react'; import PropTypes from 'prop-types'; - import sdk from '../../../index'; import { _t, _td } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import * as ServerType from '../../views/auth/ServerTypeSelector'; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // Phases // Show controls to configure server details @@ -46,18 +45,7 @@ module.exports = React.createClass({ sessionId: PropTypes.string, makeRegistrationUrl: PropTypes.func.isRequired, idSid: PropTypes.string, - // The default server name to use when the user hasn't specified - // one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this - // via `.well-known` discovery. The server name is used instead of the - // HS URL when talking about "your account". - defaultServerName: PropTypes.string, - // An error passed along from higher up explaining that something - // went wrong when finding the defaultHsUrl. - defaultServerDiscoveryError: PropTypes.string, - customHsUrl: PropTypes.string, - customIsUrl: PropTypes.string, - defaultHsUrl: PropTypes.string, - defaultIsUrl: PropTypes.string, + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, brand: PropTypes.string, email: PropTypes.string, // registration shouldn't know or care how login is done. @@ -66,7 +54,7 @@ module.exports = React.createClass({ }, getInitialState: function() { - const serverType = ServerType.getTypeFromHsUrl(this.props.customHsUrl); + const serverType = ServerType.getTypeFromServerConfig(this.props.serverConfig); return { busy: false, @@ -87,8 +75,6 @@ module.exports = React.createClass({ // straight back into UI auth doingUIAuth: Boolean(this.props.sessionId), serverType, - hsUrl: this.props.customHsUrl, - isUrl: this.props.customIsUrl, // Phase of the overall registration dialog. phase: PHASE_REGISTRATION, flows: null, @@ -100,18 +86,22 @@ module.exports = React.createClass({ this._replaceClient(); }, - onServerConfigChange: function(config) { - const newState = {}; - if (config.hsUrl !== undefined) { - newState.hsUrl = config.hsUrl; + componentWillReceiveProps(newProps) { + if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl && + newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return; + + this._replaceClient(newProps.serverConfig); + + // Handle cases where the user enters "https://matrix.org" for their server + // from the advanced option - we should default to FREE at that point. + const serverType = ServerType.getTypeFromServerConfig(newProps.serverConfig); + if (serverType !== this.state.serverType) { + // Reset the phase to default phase for the server type. + this.setState({ + serverType, + phase: this.getDefaultPhaseForServerType(serverType), + }); } - if (config.isUrl !== undefined) { - newState.isUrl = config.isUrl; - } - this.props.onServerConfigChange(config); - this.setState(newState, () => { - this._replaceClient(); - }); }, getDefaultPhaseForServerType(type) { @@ -136,19 +126,17 @@ module.exports = React.createClass({ // the new type. switch (type) { case ServerType.FREE: { - const { hsUrl, isUrl } = ServerType.TYPES.FREE; - this.onServerConfigChange({ - hsUrl, - isUrl, - }); + const { serverConfig } = ServerType.TYPES.FREE; + this.props.onServerConfigChange(serverConfig); break; } case ServerType.PREMIUM: + // We can accept whatever server config was the default here as this essentially + // acts as a slightly different "custom server"/ADVANCED option. + break; case ServerType.ADVANCED: - this.onServerConfigChange({ - hsUrl: this.props.defaultHsUrl, - isUrl: this.props.defaultIsUrl, - }); + // Use the default config from the config + this.props.onServerConfigChange(SdkConfig.get()["validated_server_config"]); break; } @@ -158,13 +146,15 @@ module.exports = React.createClass({ }); }, - _replaceClient: async function() { + _replaceClient: async function(serverConfig) { this.setState({ errorText: null, }); + if (!serverConfig) serverConfig = this.props.serverConfig; + const {hsUrl, isUrl} = serverConfig; this._matrixClient = Matrix.createClient({ - baseUrl: this.state.hsUrl, - idBaseUrl: this.state.isUrl, + baseUrl: hsUrl, + idBaseUrl: isUrl, }); try { await this._makeRegisterRequest({}); @@ -189,12 +179,6 @@ module.exports = React.createClass({ }, onFormSubmit: function(formVals) { - // Don't allow the user to register if there's a discovery error - // Without this, the user could end up registering on the wrong homeserver. - if (this.props.defaultServerDiscoveryError) { - this.setState({errorText: this.props.defaultServerDiscoveryError}); - return; - } this.setState({ errorText: "", busy: true, @@ -207,7 +191,7 @@ module.exports = React.createClass({ if (!success) { let msg = response.message || response.toString(); // can we give a better error message? - if (response.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') { + if (response.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { const errorTop = messageForResourceLimitError( response.data.limit_type, response.data.admin_contact, { @@ -302,8 +286,13 @@ module.exports = React.createClass({ }); }, - onServerDetailsNextPhaseClick(ev) { + async onServerDetailsNextPhaseClick(ev) { ev.stopPropagation(); + // TODO: TravisR - Capture the user's input somehow else + if (this._serverConfigRef) { + // Just to make sure the user's input gets captured + await this._serverConfigRef.validateServer(); + } this.setState({ phase: PHASE_REGISTRATION, }); @@ -371,20 +360,17 @@ module.exports = React.createClass({ break; case ServerType.PREMIUM: serverDetails = this._serverConfigRef = r} + serverConfig={this.props.serverConfig} + onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} />; break; case ServerType.ADVANCED: serverDetails = this._serverConfigRef = r} + serverConfig={this.props.serverConfig} + onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} />; break; @@ -392,6 +378,7 @@ module.exports = React.createClass({ let nextButton = null; if (PHASES_ENABLED) { + // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? nextButton = @@ -466,7 +453,7 @@ module.exports = React.createClass({ const AuthPage = sdk.getComponent('auth.AuthPage'); let errorText; - const err = this.state.errorText || this.props.defaultServerDiscoveryError; + const err = this.state.errorText; if (err) { errorText =
{ err }
; } From b6e027f5cb101d5f70f05161852744eaf32bc401 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:05:59 -0600 Subject: [PATCH 005/273] Make password resets use server config objects Like registration, the idea is that the object is passed around between components so they can take details they need. --- .../structures/auth/ForgotPassword.js | 88 +++++-------------- 1 file changed, 23 insertions(+), 65 deletions(-) diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 46071f0a9c..5316235fe0 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -21,8 +21,8 @@ import { _t } from '../../../languageHandler'; import sdk from '../../../index'; import Modal from "../../../Modal"; import SdkConfig from "../../../SdkConfig"; - import PasswordReset from "../../../PasswordReset"; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // Phases // Show controls to configure server details @@ -40,28 +40,14 @@ module.exports = React.createClass({ displayName: 'ForgotPassword', propTypes: { - // The default server name to use when the user hasn't specified - // one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this - // via `.well-known` discovery. The server name is used instead of the - // HS URL when talking about "your account". - defaultServerName: PropTypes.string, - // An error passed along from higher up explaining that something - // went wrong when finding the defaultHsUrl. - defaultServerDiscoveryError: PropTypes.string, - - defaultHsUrl: PropTypes.string, - defaultIsUrl: PropTypes.string, - customHsUrl: PropTypes.string, - customIsUrl: PropTypes.string, - + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, + onServerConfigChange: PropTypes.func.isRequired, onLoginClick: PropTypes.func, onComplete: PropTypes.func.isRequired, }, getInitialState: function() { return { - enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl, - enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl, phase: PHASE_FORGOT, email: "", password: "", @@ -70,11 +56,11 @@ module.exports = React.createClass({ }; }, - submitPasswordReset: function(hsUrl, identityUrl, email, password) { + submitPasswordReset: function(email, password) { this.setState({ phase: PHASE_SENDING_EMAIL, }); - this.reset = new PasswordReset(hsUrl, identityUrl); + this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl); this.reset.resetPassword(email, password).done(() => { this.setState({ phase: PHASE_EMAIL_SENT, @@ -103,13 +89,6 @@ module.exports = React.createClass({ onSubmitForm: function(ev) { ev.preventDefault(); - // Don't allow the user to register if there's a discovery error - // Without this, the user could end up registering on the wrong homeserver. - if (this.props.defaultServerDiscoveryError) { - this.setState({errorText: this.props.defaultServerDiscoveryError}); - return; - } - if (!this.state.email) { this.showErrorDialog(_t('The email address linked to your account must be entered.')); } else if (!this.state.password || !this.state.password2) { @@ -132,10 +111,7 @@ module.exports = React.createClass({ button: _t('Continue'), onFinished: (confirmed) => { if (confirmed) { - this.submitPasswordReset( - this.state.enteredHsUrl, this.state.enteredIsUrl, - this.state.email, this.state.password, - ); + this.submitPasswordReset(this.state.email, this.state.password); } }, }); @@ -148,19 +124,13 @@ module.exports = React.createClass({ }); }, - onServerConfigChange: function(config) { - const newState = {}; - if (config.hsUrl !== undefined) { - newState.enteredHsUrl = config.hsUrl; - } - if (config.isUrl !== undefined) { - newState.enteredIsUrl = config.isUrl; - } - this.setState(newState); - }, - - onServerDetailsNextPhaseClick(ev) { + async onServerDetailsNextPhaseClick(ev) { ev.stopPropagation(); + // TODO: TravisR - Capture the user's input somehow else + if (this._serverConfigRef) { + // Just to make sure the user's input gets captured + await this._serverConfigRef.validateServer(); + } this.setState({ phase: PHASE_FORGOT, }); @@ -196,13 +166,12 @@ module.exports = React.createClass({ return null; } + // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? return
this._serverConfigRef = r} + serverConfig={this.props.serverConfig} + onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={0} /> { err }
; } - let yourMatrixAccountText = _t('Your Matrix account'); - if (this.state.enteredHsUrl === this.props.defaultHsUrl && this.props.defaultServerName) { - yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', { - serverName: this.props.defaultServerName, + let yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', { + serverName: this.props.serverConfig.hsName, + }); + if (this.props.serverConfig.hsNameIsDifferent) { + // TODO: TravisR - Use tooltip to underline + yourMatrixAccountText = _t('Your Matrix account on ', {}, { + 'underlinedServerName': () => {this.props.serverConfig.hsName}, }); - } else { - try { - const parsedHsUrl = new URL(this.state.enteredHsUrl); - yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', { - serverName: parsedHsUrl.hostname, - }); - } catch (e) { - errorText =
{_t( - "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please " + - "enter a valid URL including the protocol prefix.", - { - hsUrl: this.state.enteredHsUrl, - })}
; - } } // If custom URLs are allowed, wire up the server details edit link. From 0b1a0c77b7fe9a9eef7f134041cb438ac6ad47c6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:07:40 -0600 Subject: [PATCH 006/273] Make login pass around server config objects Very similar to password resets and registration, the components pass around a server config for usage by other components. Login is a bit more complicated and needs a few more changes to pull the logic out to a more generic layer. --- src/components/structures/auth/Login.js | 189 ++++++--------------- src/components/views/auth/PasswordLogin.js | 77 ++++----- 2 files changed, 86 insertions(+), 180 deletions(-) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 2940346a4f..46bf0c2c76 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -25,7 +25,7 @@ import sdk from '../../../index'; import Login from '../../../Login'; import SdkConfig from '../../../SdkConfig'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; -import { AutoDiscovery } from "matrix-js-sdk"; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // For validating phone numbers without country codes const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/; @@ -59,19 +59,14 @@ module.exports = React.createClass({ propTypes: { onLoggedIn: PropTypes.func.isRequired, - // The default server name to use when the user hasn't specified - // one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this - // via `.well-known` discovery. The server name is used instead of the - // HS URL when talking about where to "sign in to". - defaultServerName: PropTypes.string, // An error passed along from higher up explaining that something - // went wrong when finding the defaultHsUrl. - defaultServerDiscoveryError: PropTypes.string, + // went wrong. May be replaced with a different error within the + // Login component. + errorText: PropTypes.string, + + // If true, the component will consider itself busy. + busy: PropTypes.bool, - customHsUrl: PropTypes.string, - customIsUrl: PropTypes.string, - defaultHsUrl: PropTypes.string, - defaultIsUrl: PropTypes.string, // Secondary HS which we try to log into if the user is using // the default HS but login fails. Useful for migrating to a // different homeserver without confusing users. @@ -79,12 +74,13 @@ module.exports = React.createClass({ defaultDeviceDisplayName: PropTypes.string, - // login shouldn't know or care how registration is done. + // login shouldn't know or care how registration, password recovery, + // etc is done. onRegisterClick: PropTypes.func.isRequired, - - // login shouldn't care how password recovery is done. onForgotPasswordClick: PropTypes.func, onServerConfigChange: PropTypes.func.isRequired, + + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, }, getInitialState: function() { @@ -93,9 +89,6 @@ module.exports = React.createClass({ errorText: null, loginIncorrect: false, - enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl, - enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl, - // used for preserving form values when changing homeserver username: "", phoneCountry: null, @@ -105,10 +98,6 @@ module.exports = React.createClass({ phase: PHASE_LOGIN, // The current login flow, such as password, SSO, etc. currentFlow: "m.login.password", - - // .well-known discovery - discoveryError: "", - findingHomeserver: false, }; }, @@ -139,10 +128,17 @@ module.exports = React.createClass({ }); }, + isBusy: function() { + return this.state.busy || this.props.busy; + }, + + hasError: function() { + return this.state.errorText || this.props.errorText; + }, + onPasswordLogin: function(username, phoneCountry, phoneNumber, password) { - // Prevent people from submitting their password when homeserver - // discovery went wrong - if (this.state.discoveryError || this.props.defaultServerDiscoveryError) return; + // Prevent people from submitting their password when something isn't right. + if (this.isBusy() || this.hasError()) return; this.setState({ busy: true, @@ -164,7 +160,7 @@ module.exports = React.createClass({ const usingEmail = username.indexOf("@") > 0; if (error.httpStatus === 400 && usingEmail) { errorText = _t('This homeserver does not support login using email address.'); - } else if (error.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') { + } else if (error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { const errorTop = messageForResourceLimitError( error.data.limit_type, error.data.admin_contact, { @@ -194,11 +190,10 @@ module.exports = React.createClass({
{ _t('Incorrect username and/or password.') }
- { _t('Please note you are logging into the %(hs)s server, not matrix.org.', - { - hs: this.props.defaultHsUrl.replace(/^https?:\/\//, ''), - }) - } + {_t( + 'Please note you are logging into the %(hs)s server, not matrix.org.', + {hs: this.props.serverConfig.hsName}, + )}
); @@ -235,9 +230,9 @@ module.exports = React.createClass({ onUsernameBlur: function(username) { this.setState({ username: username, - discoveryError: null, + errorText: null, }); - if (username[0] === "@") { + if (username[0] === "@" && false) { // TODO: TravisR - Restore this const serverName = username.split(':').slice(1).join(':'); try { // we have to append 'https://' to make the URL constructor happy @@ -246,7 +241,7 @@ module.exports = React.createClass({ this._tryWellKnownDiscovery(url.hostname); } catch (e) { console.error("Problem parsing URL or unhandled error doing .well-known discovery:", e); - this.setState({discoveryError: _t("Failed to perform homeserver discovery")}); + this.setState({errorText: _t("Failed to perform homeserver discovery")}); } } }, @@ -274,32 +269,19 @@ module.exports = React.createClass({ } }, - onServerConfigChange: function(config) { - const self = this; - const newState = { - errorText: null, // reset err messages - }; - if (config.hsUrl !== undefined) { - newState.enteredHsUrl = config.hsUrl; - } - if (config.isUrl !== undefined) { - newState.enteredIsUrl = config.isUrl; - } - - this.props.onServerConfigChange(config); - this.setState(newState, function() { - self._initLoginLogic(config.hsUrl || null, config.isUrl); - }); - }, - onRegisterClick: function(ev) { ev.preventDefault(); ev.stopPropagation(); this.props.onRegisterClick(); }, - onServerDetailsNextPhaseClick(ev) { + async onServerDetailsNextPhaseClick(ev) { ev.stopPropagation(); + // TODO: TravisR - Capture the user's input somehow else + if (this._serverConfigRef) { + // Just to make sure the user's input gets captured + await this._serverConfigRef.validateServer(); + } this.setState({ phase: PHASE_LOGIN, }); @@ -313,64 +295,13 @@ module.exports = React.createClass({ }); }, - _tryWellKnownDiscovery: async function(serverName) { - if (!serverName.trim()) { - // Nothing to discover - this.setState({ - discoveryError: "", - findingHomeserver: false, - }); - return; - } - - this.setState({findingHomeserver: true}); - try { - const discovery = await AutoDiscovery.findClientConfig(serverName); - - const state = discovery["m.homeserver"].state; - if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) { - this.setState({ - discoveryError: discovery["m.homeserver"].error, - findingHomeserver: false, - }); - } else if (state === AutoDiscovery.PROMPT) { - this.setState({ - discoveryError: "", - findingHomeserver: false, - }); - } else if (state === AutoDiscovery.SUCCESS) { - this.setState({ - discoveryError: "", - findingHomeserver: false, - }); - this.onServerConfigChange({ - hsUrl: discovery["m.homeserver"].base_url, - isUrl: discovery["m.identity_server"].state === AutoDiscovery.SUCCESS - ? discovery["m.identity_server"].base_url - : "", - }); - } else { - console.warn("Unknown state for m.homeserver in discovery response: ", discovery); - this.setState({ - discoveryError: _t("Unknown failure discovering homeserver"), - findingHomeserver: false, - }); - } - } catch (e) { - console.error(e); - this.setState({ - findingHomeserver: false, - discoveryError: _t("Unknown error discovering homeserver"), - }); - } - }, - _initLoginLogic: function(hsUrl, isUrl) { const self = this; - hsUrl = hsUrl || this.state.enteredHsUrl; - isUrl = isUrl || this.state.enteredIsUrl; + hsUrl = hsUrl || this.props.serverConfig.hsUrl; + isUrl = isUrl || this.props.serverConfig.isUrl; - const fallbackHsUrl = hsUrl === this.props.defaultHsUrl ? this.props.fallbackHsUrl : null; + // TODO: TravisR - Only use this if the homeserver is the default homeserver + const fallbackHsUrl = this.props.fallbackHsUrl; const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, { defaultDeviceDisplayName: this.props.defaultDeviceDisplayName, @@ -378,8 +309,6 @@ module.exports = React.createClass({ this._loginLogic = loginLogic; this.setState({ - enteredHsUrl: hsUrl, - enteredIsUrl: isUrl, busy: true, loginIncorrect: false, }); @@ -445,8 +374,8 @@ module.exports = React.createClass({ if (err.cors === 'rejected') { if (window.location.protocol === 'https:' && - (this.state.enteredHsUrl.startsWith("http:") || - !this.state.enteredHsUrl.startsWith("http")) + (this.props.serverConfig.hsUrl.startsWith("http:") || + !this.props.serverConfig.hsUrl.startsWith("http")) ) { errorText = { _t("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " + @@ -469,9 +398,9 @@ module.exports = React.createClass({ "is not blocking requests.", {}, { 'a': (sub) => { - return { sub }; + return + { sub } + ; }, }, ) } @@ -495,19 +424,17 @@ module.exports = React.createClass({ } const serverDetails = this._serverConfigRef = r} + serverConfig={this.props.serverConfig} + onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} />; let nextButton = null; if (PHASES_ENABLED) { + // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? nextButton = + onClick={this.onServerDetailsNextPhaseClick}> {_t("Next")} ; } @@ -547,13 +474,6 @@ module.exports = React.createClass({ onEditServerDetailsClick = this.onEditServerDetailsClick; } - // If the current HS URL is the default HS URL, then we can label it - // with the default HS name (if it exists). - let hsName; - if (this.state.enteredHsUrl === this.props.defaultHsUrl) { - hsName = this.props.defaultServerName; - } - return ( + serverConfig={this.props.serverConfig} + disableSubmit={this.isBusy()} + /> ); }, @@ -595,9 +514,9 @@ module.exports = React.createClass({ const AuthPage = sdk.getComponent("auth.AuthPage"); const AuthHeader = sdk.getComponent("auth.AuthHeader"); const AuthBody = sdk.getComponent("auth.AuthBody"); - const loader = this.state.busy ?
: null; + const loader = this.isBusy() ?
: null; - const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText; + const errorText = this.state.errorText || this.props.errorText; let errorTextSection; if (errorText) { diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index ed3afede2f..90c607442f 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -1,6 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd +Copyright 2017,2019 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,11 +21,29 @@ import classNames from 'classnames'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; +import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; /** * A pure UI component which displays a username/password form. */ -class PasswordLogin extends React.Component { +export default class PasswordLogin extends React.Component { + static propTypes = { + onSubmit: PropTypes.func.isRequired, // fn(username, password) + onError: PropTypes.func, + onForgotPasswordClick: PropTypes.func, // fn() + initialUsername: PropTypes.string, + initialPhoneCountry: PropTypes.string, + initialPhoneNumber: PropTypes.string, + initialPassword: PropTypes.string, + onUsernameChanged: PropTypes.func, + onPhoneCountryChanged: PropTypes.func, + onPhoneNumberChanged: PropTypes.func, + onPasswordChanged: PropTypes.func, + loginIncorrect: PropTypes.bool, + disableSubmit: PropTypes.bool, + serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, + }; + static defaultProps = { onError: function() {}, onEditServerDetailsClick: null, @@ -40,13 +58,12 @@ class PasswordLogin extends React.Component { initialPhoneNumber: "", initialPassword: "", loginIncorrect: false, - // This is optional and only set if we used a server name to determine - // the HS URL via `.well-known` discovery. The server name is used - // instead of the HS URL when talking about where to "sign in to". - hsName: null, - hsUrl: "", disableSubmit: false, - } + }; + + static LOGIN_FIELD_EMAIL = "login_field_email"; + static LOGIN_FIELD_MXID = "login_field_mxid"; + static LOGIN_FIELD_PHONE = "login_field_phone"; constructor(props) { super(props); @@ -258,20 +275,14 @@ class PasswordLogin extends React.Component {
; } - let signInToText = _t('Sign in to your Matrix account'); - if (this.props.hsName) { - signInToText = _t('Sign in to your Matrix account on %(serverName)s', { - serverName: this.props.hsName, + let signInToText = _t('Sign in to your Matrix account on %(serverName)s', { + serverName: this.props.serverConfig.hsName, + }); + if (this.props.serverConfig.hsNameIsDifferent) { + // TODO: TravisR - Use tooltip to underline + signInToText = _t('Sign in to your Matrix account on ', {}, { + 'underlinedServerName': () => {this.props.serverConfig.hsName}, }); - } else { - try { - const parsedHsUrl = new URL(this.props.hsUrl); - signInToText = _t('Sign in to your Matrix account on %(serverName)s', { - serverName: parsedHsUrl.hostname, - }); - } catch (e) { - // ignore - } } let editLink = null; @@ -353,27 +364,3 @@ class PasswordLogin extends React.Component { ); } } - -PasswordLogin.LOGIN_FIELD_EMAIL = "login_field_email"; -PasswordLogin.LOGIN_FIELD_MXID = "login_field_mxid"; -PasswordLogin.LOGIN_FIELD_PHONE = "login_field_phone"; - -PasswordLogin.propTypes = { - onSubmit: PropTypes.func.isRequired, // fn(username, password) - onError: PropTypes.func, - onForgotPasswordClick: PropTypes.func, // fn() - initialUsername: PropTypes.string, - initialPhoneCountry: PropTypes.string, - initialPhoneNumber: PropTypes.string, - initialPassword: PropTypes.string, - onUsernameChanged: PropTypes.func, - onPhoneCountryChanged: PropTypes.func, - onPhoneNumberChanged: PropTypes.func, - onPasswordChanged: PropTypes.func, - loginIncorrect: PropTypes.bool, - hsName: PropTypes.string, - hsUrl: PropTypes.string, - disableSubmit: PropTypes.bool, -}; - -module.exports = PasswordLogin; From 1f527e71b1296a5a5747160f1e0fbf7aea27bbfd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:09:07 -0600 Subject: [PATCH 007/273] Bring server config juggling into MatrixChat This way the server config is consistent across login, password reset, and registration. This also brings the code into a more generic place for all 3 duplicated efforts. --- src/components/structures/MatrixChat.js | 155 +++--------------------- 1 file changed, 17 insertions(+), 138 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 277985ba1d..ca9ddec749 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -50,8 +50,7 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; import ResizeNotifier from "../../utils/ResizeNotifier"; - -const AutoDiscovery = Matrix.AutoDiscovery; +import {ValidatedDiscoveryConfig} from "../../utils/AutoDiscoveryUtils"; // Disable warnings for now: we use deprecated bluebird functions // and need to migrate, but they spam the console with warnings. @@ -181,16 +180,8 @@ export default React.createClass({ // Parameters used in the registration dance with the IS register_client_secret: null, register_session_id: null, - register_hs_url: null, - register_is_url: null, register_id_sid: null, - // Parameters used for setting up the authentication views - defaultServerName: this.props.config.default_server_name, - defaultHsUrl: this.props.config.default_hs_url, - defaultIsUrl: this.props.config.default_is_url, - defaultServerDiscoveryError: null, - // When showing Modal dialogs we need to set aria-hidden on the root app element // and disable it when there are no dialogs hideToSRUsers: false, @@ -211,42 +202,15 @@ export default React.createClass({ }; }, - getDefaultServerName: function() { - return this.state.defaultServerName; - }, - - getCurrentHsUrl: function() { - if (this.state.register_hs_url) { - return this.state.register_hs_url; - } else if (MatrixClientPeg.get()) { - return MatrixClientPeg.get().getHomeserverUrl(); - } else { - return this.getDefaultHsUrl(); - } - }, - - getDefaultHsUrl(defaultToMatrixDotOrg) { - defaultToMatrixDotOrg = typeof(defaultToMatrixDotOrg) !== 'boolean' ? true : defaultToMatrixDotOrg; - if (!this.state.defaultHsUrl && defaultToMatrixDotOrg) return "https://matrix.org"; - return this.state.defaultHsUrl; - }, - + // TODO: TravisR - Remove this or put it somewhere else getFallbackHsUrl: function() { return this.props.config.fallback_hs_url; }, - getCurrentIsUrl: function() { - if (this.state.register_is_url) { - return this.state.register_is_url; - } else if (MatrixClientPeg.get()) { - return MatrixClientPeg.get().getIdentityServerUrl(); - } else { - return this.getDefaultIsUrl(); - } - }, - - getDefaultIsUrl() { - return this.state.defaultIsUrl || "https://vector.im"; + getServerProperties() { + let props = this.state.serverConfig; + if (!props) props = SdkConfig.get()["validated_server_config"]; + return {serverConfig: props}; }, componentWillMount: function() { @@ -260,40 +224,6 @@ export default React.createClass({ MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit; } - // Set up the default URLs (async) - if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) { - this.setState({loadingDefaultHomeserver: true}); - this._tryDiscoverDefaultHomeserver(this.getDefaultServerName()); - } else if (this.getDefaultServerName() && this.getDefaultHsUrl(false)) { - // Ideally we would somehow only communicate this to the server admins, but - // given this is at login time we can't really do much besides hope that people - // will check their settings. - this.setState({ - defaultServerName: null, // To un-hide any secrets people might be keeping - defaultServerDiscoveryError: _t( - "Invalid configuration: Cannot supply a default homeserver URL and " + - "a default server name", - ), - }); - } - - // Set a default HS with query param `hs_url` - const paramHs = this.props.startingFragmentQueryParams.hs_url; - if (paramHs) { - console.log('Setting register_hs_url ', paramHs); - this.setState({ - register_hs_url: paramHs, - }); - } - // Set a default IS with query param `is_url` - const paramIs = this.props.startingFragmentQueryParams.is_url; - if (paramIs) { - console.log('Setting register_is_url ', paramIs); - this.setState({ - register_is_url: paramIs, - }); - } - // a thing to call showScreen with once login completes. this is kept // outside this.state because updating it should never trigger a // rerender. @@ -374,8 +304,8 @@ export default React.createClass({ return Lifecycle.loadSession({ fragmentQueryParams: this.props.startingFragmentQueryParams, enableGuest: this.props.enableGuest, - guestHsUrl: this.getCurrentHsUrl(), - guestIsUrl: this.getCurrentIsUrl(), + guestHsUrl: this.getServerProperties().serverConfig.hsUrl, + guestIsUrl: this.getServerProperties().serverConfig.isUrl, defaultDeviceDisplayName: this.props.defaultDeviceDisplayName, }); }).then((loadedSession) => { @@ -1823,44 +1753,7 @@ export default React.createClass({ }, onServerConfigChange(config) { - const newState = {}; - if (config.hsUrl) { - newState.register_hs_url = config.hsUrl; - } - if (config.isUrl) { - newState.register_is_url = config.isUrl; - } - this.setState(newState); - }, - - _tryDiscoverDefaultHomeserver: async function(serverName) { - try { - const discovery = await AutoDiscovery.findClientConfig(serverName); - const state = discovery["m.homeserver"].state; - if (state !== AutoDiscovery.SUCCESS) { - console.error("Failed to discover homeserver on startup:", discovery); - this.setState({ - defaultServerDiscoveryError: discovery["m.homeserver"].error, - loadingDefaultHomeserver: false, - }); - } else { - const hsUrl = discovery["m.homeserver"].base_url; - const isUrl = discovery["m.identity_server"].state === AutoDiscovery.SUCCESS - ? discovery["m.identity_server"].base_url - : "https://vector.im"; - this.setState({ - defaultHsUrl: hsUrl, - defaultIsUrl: isUrl, - loadingDefaultHomeserver: false, - }); - } - } catch (e) { - console.error(e); - this.setState({ - defaultServerDiscoveryError: _t("Unknown error discovering homeserver"), - loadingDefaultHomeserver: false, - }); - } + this.setState({serverConfig: config}); }, _makeRegistrationUrl: function(params) { @@ -1879,8 +1772,7 @@ export default React.createClass({ if ( this.state.view === VIEWS.LOADING || - this.state.view === VIEWS.LOGGING_IN || - this.state.loadingDefaultHomeserver + this.state.view === VIEWS.LOGGING_IN ) { const Spinner = sdk.getComponent('elements.Spinner'); return ( @@ -1958,18 +1850,13 @@ export default React.createClass({ sessionId={this.state.register_session_id} idSid={this.state.register_id_sid} email={this.props.startingFragmentQueryParams.email} - defaultServerName={this.getDefaultServerName()} - defaultServerDiscoveryError={this.state.defaultServerDiscoveryError} - defaultHsUrl={this.getDefaultHsUrl()} - defaultIsUrl={this.getDefaultIsUrl()} brand={this.props.config.brand} - customHsUrl={this.getCurrentHsUrl()} - customIsUrl={this.getCurrentIsUrl()} makeRegistrationUrl={this._makeRegistrationUrl} onLoggedIn={this.onRegistered} onLoginClick={this.onLoginClick} onServerConfigChange={this.onServerConfigChange} - /> + {...this.getServerProperties()} + /> ); } @@ -1978,14 +1865,11 @@ export default React.createClass({ const ForgotPassword = sdk.getComponent('structures.auth.ForgotPassword'); return ( + onLoginClick={this.onLoginClick} + onServerConfigChange={this.onServerConfigChange} + {...this.getServerProperties()} + /> ); } @@ -1995,16 +1879,11 @@ export default React.createClass({ ); } From bb6ee10d8c4ac46c8547ae3263a352ef77e6886b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:09:31 -0600 Subject: [PATCH 008/273] Add language features to support server config changes --- src/i18n/strings/en_EN.json | 13 ++++++------- src/languageHandler.js | 12 ++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index eaea057b36..35593708c8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -246,6 +246,8 @@ "%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …", "%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …", "%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …", + "No homeserver URL provided": "No homeserver URL provided", + "Unexpected error resolving homeserver configuration": "Unexpected error resolving homeserver configuration", "This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.", "This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.", "Please contact your service administrator to continue using the service.": "Please contact your service administrator to continue using the service.", @@ -1306,6 +1308,7 @@ "Code": "Code", "Submit": "Submit", "Start authentication": "Start authentication", + "Unable to validate homeserver/identity server": "Unable to validate homeserver/identity server", "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 modular.im.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.", "Server Name": "Server Name", @@ -1318,8 +1321,8 @@ "Username": "Username", "Phone": "Phone", "Not sure of your password? Set a new one": "Not sure of your password? Set a new one", - "Sign in to your Matrix account": "Sign in to your Matrix account", "Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s", + "Sign in to your Matrix account on ": "Sign in to your Matrix account on ", "Change": "Change", "Sign in with": "Sign in with", "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?", @@ -1339,8 +1342,8 @@ "Email (optional)": "Email (optional)", "Confirm": "Confirm", "Phone (optional)": "Phone (optional)", - "Create your Matrix account": "Create your Matrix account", "Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s", + "Create your Matrix account on ": "Create your Matrix account on ", "Use an email address to recover your account.": "Use an email address to recover your account.", "Other users can invite you to rooms using your contact details.": "Other users can invite you to rooms using your contact details.", "Other servers": "Other servers", @@ -1407,7 +1410,6 @@ "This homeserver does not support communities": "This homeserver does not support communities", "Failed to load %(groupId)s": "Failed to load %(groupId)s", "Filter room names": "Filter room names", - "Invalid configuration: Cannot supply a default homeserver URL and a default server name": "Invalid configuration: Cannot supply a default homeserver URL and a default server name", "Failed to reject invitation": "Failed to reject invitation", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", @@ -1423,7 +1425,6 @@ "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.", "You are logged in to another account": "You are logged in to another account", "Thank you for verifying your email! The account you're logged into here (%(sessionUserId)s) appears to be different from the account you've verified an email for (%(verifiedUserId)s). If you would like to log in to %(verifiedUserId2)s, please log out first.": "Thank you for verifying your email! The account you're logged into here (%(sessionUserId)s) appears to be different from the account you've verified an email for (%(verifiedUserId)s). If you would like to log in to %(verifiedUserId2)s, please log out first.", - "Unknown error discovering homeserver": "Unknown error discovering homeserver", "Logout": "Logout", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your Riot.im experience!": "Did you know: you can use communities to filter your Riot.im experience!", @@ -1491,9 +1492,8 @@ "A new password must be entered.": "A new password must be entered.", "New passwords must match each other.": "New passwords must match each other.", "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.", - "Your Matrix account": "Your Matrix account", "Your Matrix account on %(serverName)s": "Your Matrix account on %(serverName)s", - "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.": "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please enter a valid URL including the protocol prefix.", + "Your Matrix account on ": "Your Matrix account on ", "A verification email will be sent to your inbox to confirm setting your new password.": "A verification email will be sent to your inbox to confirm setting your new password.", "Send Reset Email": "Send Reset Email", "Sign in instead": "Sign in instead", @@ -1517,7 +1517,6 @@ "Please note you are logging into the %(hs)s server, not matrix.org.": "Please note you are logging into the %(hs)s server, not matrix.org.", "Failed to perform homeserver discovery": "Failed to perform homeserver discovery", "The phone number entered looks invalid": "The phone number entered looks invalid", - "Unknown failure discovering homeserver": "Unknown failure discovering homeserver", "This homeserver doesn't offer any login flows which are supported by this client.": "This homeserver doesn't offer any login flows which are supported by this client.", "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", diff --git a/src/languageHandler.js b/src/languageHandler.js index 854ac079bc..bd3a8df721 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -32,6 +32,18 @@ counterpart.setSeparator('|'); // Fall back to English counterpart.setFallbackLocale('en'); +/** + * Helper function to create an error which has an English message + * with a translatedMessage property for use by the consumer. + * @param {string} message Message to translate. + * @returns {Error} The constructed error. + */ +export function newTranslatableError(message) { + const error = new Error(message); + error.translatedMessage = _t(message); + return error; +} + // Function which only purpose is to mark that a string is translatable // Does not actually do anything. It's helpful for automatic extraction of translatable strings export function _td(s) { From a4b64649029b1f51a2da2d9143ccade636039ccd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:39:15 -0600 Subject: [PATCH 009/273] Appease the linter --- src/components/structures/MatrixChat.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index ca9ddec749..089c843e6f 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -50,7 +50,6 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; import ResizeNotifier from "../../utils/ResizeNotifier"; -import {ValidatedDiscoveryConfig} from "../../utils/AutoDiscoveryUtils"; // Disable warnings for now: we use deprecated bluebird functions // and need to migrate, but they spam the console with warnings. From ae63df95ea09ec4be2f9a50bf4988ed4d98a16ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:46:43 -0600 Subject: [PATCH 010/273] Fix tests to use new serverConfig prop --- test/components/structures/auth/Login-test.js | 4 ++-- test/components/structures/auth/Registration-test.js | 4 ++-- test/test-utils.js | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/test/components/structures/auth/Login-test.js b/test/components/structures/auth/Login-test.js index ec95243a56..74451b922f 100644 --- a/test/components/structures/auth/Login-test.js +++ b/test/components/structures/auth/Login-test.js @@ -22,6 +22,7 @@ import ReactTestUtils from 'react-dom/test-utils'; import sdk from 'matrix-react-sdk'; import SdkConfig from '../../../../src/SdkConfig'; import * as TestUtils from '../../../test-utils'; +import {mkServerConfig} from "../../../test-utils"; const Login = sdk.getComponent( 'structures.auth.Login', @@ -44,8 +45,7 @@ describe('Login', function() { function render() { return ReactDOM.render( {}} onRegisterClick={() => {}} onServerConfigChange={() => {}} diff --git a/test/components/structures/auth/Registration-test.js b/test/components/structures/auth/Registration-test.js index a10201d465..6914ed71d7 100644 --- a/test/components/structures/auth/Registration-test.js +++ b/test/components/structures/auth/Registration-test.js @@ -22,6 +22,7 @@ import ReactTestUtils from 'react-dom/test-utils'; import sdk from 'matrix-react-sdk'; import SdkConfig from '../../../../src/SdkConfig'; import * as TestUtils from '../../../test-utils'; +import {mkServerConfig} from "../../../test-utils"; const Registration = sdk.getComponent( 'structures.auth.Registration', @@ -44,8 +45,7 @@ describe('Registration', function() { function render() { return ReactDOM.render( {}} onLoggedIn={() => {}} onLoginClick={() => {}} diff --git a/test/test-utils.js b/test/test-utils.js index f4f00effbb..54705434e2 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -7,6 +7,8 @@ import PropTypes from 'prop-types'; import peg from '../src/MatrixClientPeg'; import dis from '../src/dispatcher'; import jssdk from 'matrix-js-sdk'; +import {makeType} from "../src/utils/TypeUtils"; +import {ValidatedServerConfig} from "../src/utils/AutoDiscoveryUtils"; const MatrixEvent = jssdk.MatrixEvent; /** @@ -260,6 +262,16 @@ export function mkStubRoom(roomId = null) { }; } +export function mkServerConfig(hsUrl, isUrl) { + return makeType(ValidatedServerConfig, { + hsUrl, + hsName: "TEST_ENVIRONMENT", + hsNameIsDifferent: false, // yes, we lie + isUrl, + identityEnabled: true, + }); +} + export function getDispatchForStore(store) { // Mock the dispatcher by gut-wrenching. Stores can only __emitChange whilst a // dispatcher `_isDispatching` is true. From 4ada66d3195052a0617f5c49d4512d764da961cc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 2 May 2019 23:59:54 -0600 Subject: [PATCH 011/273] Fix rogue instance of old hsUrl property --- src/components/views/auth/PasswordLogin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index 90c607442f..f5b2aec210 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -212,7 +212,7 @@ export default class PasswordLogin extends React.Component { type="text" label={SdkConfig.get().disable_custom_urls ? _t("Username on %(hs)s", { - hs: this.props.hsUrl.replace(/^https?:\/\//, ''), + hs: this.props.serverConfig.hsName, }) : _t("Username")} value={this.state.username} onChange={this.onUsernameChanged} From 58b9eb4cb22220c9d90f21d966843199cad55535 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 3 May 2019 16:25:54 -0600 Subject: [PATCH 012/273] Add a serverConfig property to MatrixChat for unit tests --- src/components/structures/MatrixChat.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 089c843e6f..545f847718 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -50,6 +50,7 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; import ResizeNotifier from "../../utils/ResizeNotifier"; +import {ValidatedServerConfig} from "../../utils/AutoDiscoveryUtils"; // Disable warnings for now: we use deprecated bluebird functions // and need to migrate, but they spam the console with warnings. @@ -107,6 +108,7 @@ export default React.createClass({ propTypes: { config: PropTypes.object, + serverConfig: PropTypes.instanceOf(ValidatedServerConfig), ConferenceHandler: PropTypes.any, onNewScreen: PropTypes.func, registrationUrl: PropTypes.string, @@ -208,6 +210,7 @@ export default React.createClass({ getServerProperties() { let props = this.state.serverConfig; + if (!props) props = this.props.serverConfig; // for unit tests if (!props) props = SdkConfig.get()["validated_server_config"]; return {serverConfig: props}; }, From eab209a26b5bc6eb9cf13bc082fd506dea82e10a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 6 May 2019 12:00:48 -0600 Subject: [PATCH 013/273] Log in to the right homeserver when changing the homeserver --- src/components/structures/auth/Login.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 46bf0c2c76..af9370f2db 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -121,6 +121,14 @@ module.exports = React.createClass({ this._unmounted = true; }, + componentWillReceiveProps(newProps) { + if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl && + newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return; + + // Ensure that we end up actually logging in to the right place + this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); + }, + onPasswordLoginError: function(errorText) { this.setState({ errorText, From ee33a4e9ba6349136c68a5165c14e914b0d02135 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 16:30:34 -0600 Subject: [PATCH 014/273] Refactor "Next" button into ServerConfig components TODO still remains about making ModularServerConfig extend ServerConfig instead of duplicating everything. See https://github.com/vector-im/riot-web/issues/9290 --- .../structures/auth/ForgotPassword.js | 30 +++----- src/components/structures/auth/Login.js | 38 ++++------ .../structures/auth/Registration.js | 31 +++----- .../views/auth/ModularServerConfig.js | 51 ++++++++++--- src/components/views/auth/ServerConfig.js | 71 ++++++++++++++----- 5 files changed, 125 insertions(+), 96 deletions(-) diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 5316235fe0..a772e72c5a 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -124,13 +124,7 @@ module.exports = React.createClass({ }); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_FORGOT, }); @@ -160,25 +154,19 @@ module.exports = React.createClass({ renderServerDetails() { const ServerConfig = sdk.getComponent("auth.ServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; } - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - return
- this._serverConfigRef = r} - serverConfig={this.props.serverConfig} - onServerConfigChange={this.props.onServerConfigChange} - delayTimeMs={0} /> - - {_t("Next")} - -
; + return ; }, renderForgot() { diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index af9370f2db..68b440d064 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -20,11 +20,11 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import { _t, _td } from '../../../languageHandler'; +import {_t, _td} from '../../../languageHandler'; import sdk from '../../../index'; import Login from '../../../Login'; import SdkConfig from '../../../SdkConfig'; -import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; +import {messageForResourceLimitError} from '../../../utils/ErrorUtils'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // For validating phone numbers without country codes @@ -283,13 +283,7 @@ module.exports = React.createClass({ this.props.onRegisterClick(); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_LOGIN, }); @@ -421,7 +415,6 @@ module.exports = React.createClass({ renderServerComponent() { const ServerConfig = sdk.getComponent("auth.ServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; @@ -431,26 +424,19 @@ module.exports = React.createClass({ return null; } - const serverDetails = this._serverConfigRef = r} + const serverDetailsProps = {}; + if (PHASES_ENABLED) { + serverDetailsProps.onAfterSubmit = this.onServerDetailsNextPhaseClick; + serverDetailsProps.submitText = _t("Next"); + serverDetailsProps.submitClass = "mx_Login_submit"; + } + + return ; - - let nextButton = null; - if (PHASES_ENABLED) { - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - nextButton = - {_t("Next")} - ; - } - - return
- {serverDetails} - {nextButton} -
; }, renderLoginComponentForStep() { diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index faab8190bd..f516816033 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -286,13 +286,7 @@ module.exports = React.createClass({ }); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_REGISTRATION, }); @@ -337,7 +331,6 @@ module.exports = React.createClass({ const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; @@ -354,45 +347,41 @@ module.exports = React.createClass({
; } + const serverDetailsProps = {}; + if (PHASES_ENABLED) { + serverDetailsProps.onAfterSubmit = this.onServerDetailsNextPhaseClick; + serverDetailsProps.submitText = _t("Next"); + serverDetailsProps.submitClass = "mx_Login_submit"; + } + let serverDetails = null; switch (this.state.serverType) { case ServerType.FREE: break; case ServerType.PREMIUM: serverDetails = this._serverConfigRef = r} serverConfig={this.props.serverConfig} onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} + {...serverDetailsProps} />; break; case ServerType.ADVANCED: serverDetails = this._serverConfigRef = r} serverConfig={this.props.serverConfig} onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} + {...serverDetailsProps} />; break; } - let nextButton = null; - if (PHASES_ENABLED) { - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - nextButton = - {_t("Next")} - ; - } - return
{serverDetails} - {nextButton}
; }, diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index ea22577dbd..5a3bc23596 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -41,6 +41,16 @@ export default class ModularServerConfig extends React.PureComponent { serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, delayTimeMs: PropTypes.number, // time to wait before invoking onChanged + + // Called after the component calls onServerConfigChange + onAfterSubmit: PropTypes.func, + + // Optional text for the submit button. If falsey, no button will be shown. + submitText: PropTypes.string, + + // Optional class for the submit button. Only applies if the submit button + // is to be rendered. + submitClass: PropTypes.string, }; static defaultProps = { @@ -119,6 +129,16 @@ export default class ModularServerConfig extends React.PureComponent { this.setState({ hsUrl }); }; + onSubmit = async (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + await this.validateServer(); + + if (this.props.onAfterSubmit) { + this.props.onAfterSubmit(); + } + }; + _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { clearTimeout(existingTimeoutId); @@ -128,6 +148,16 @@ export default class ModularServerConfig extends React.PureComponent { render() { const Field = sdk.getComponent('elements.Field'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + + const submitButton = this.props.submitText + ? {this.props.submitText} + : null; return (
@@ -141,15 +171,18 @@ export default class ModularServerConfig extends React.PureComponent { , }, )} -
- -
+
+
+ +
+ {submitButton} +
); } diff --git a/src/components/views/auth/ServerConfig.js b/src/components/views/auth/ServerConfig.js index 096e461efe..3967f49f18 100644 --- a/src/components/views/auth/ServerConfig.js +++ b/src/components/views/auth/ServerConfig.js @@ -30,12 +30,22 @@ import SdkConfig from "../../../SdkConfig"; export default class ServerConfig extends React.PureComponent { static propTypes = { - onServerConfigChange: PropTypes.func, + onServerConfigChange: PropTypes.func.isRequired, // The current configuration that the user is expecting to change. serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, delayTimeMs: PropTypes.number, // time to wait before invoking onChanged + + // Called after the component calls onServerConfigChange + onAfterSubmit: PropTypes.func, + + // Optional text for the submit button. If falsey, no button will be shown. + submitText: PropTypes.string, + + // Optional class for the submit button. Only applies if the submit button + // is to be rendered. + submitClass: PropTypes.string, }; static defaultProps = { @@ -124,6 +134,16 @@ export default class ServerConfig extends React.PureComponent { this.setState({ isUrl }); }; + onSubmit = async (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + await this.validateServer(); + + if (this.props.onAfterSubmit) { + this.props.onAfterSubmit(); + } + }; + _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { clearTimeout(existingTimeoutId); @@ -138,11 +158,21 @@ export default class ServerConfig extends React.PureComponent { render() { const Field = sdk.getComponent('elements.Field'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const errorText = this.state.errorText ? {this.state.errorText} : null; + const submitButton = this.props.submitText + ? {this.props.submitText} + : null; + return (

{_t("Other servers")}

@@ -152,24 +182,27 @@ export default class ServerConfig extends React.PureComponent { , })} {errorText} -
- - -
+
+
+ + +
+ {submitButton} +
); } From e4576dac28cad4e5e18b04d4f5a1dbd433dfa59f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 17:16:40 -0600 Subject: [PATCH 015/273] Render underlines and tooltips on custom server names in auth pages See https://github.com/vector-im/riot-web/issues/9290 --- res/css/_components.scss | 1 + res/css/structures/auth/_Login.scss | 4 ++ res/css/views/elements/_TextWithTooltip.scss | 19 +++++++ .../structures/auth/ForgotPassword.js | 11 +++- src/components/views/auth/PasswordLogin.js | 11 +++- src/components/views/auth/RegistrationForm.js | 11 +++- .../views/elements/TextWithTooltip.js | 56 +++++++++++++++++++ src/components/views/elements/Tooltip.js | 4 ++ 8 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 res/css/views/elements/_TextWithTooltip.scss create mode 100644 src/components/views/elements/TextWithTooltip.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 6e681894e3..ff22ad9eab 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -97,6 +97,7 @@ @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; +@import "./views/elements/_TextWithTooltip.scss"; @import "./views/elements/_ToggleSwitch.scss"; @import "./views/elements/_ToolTipButton.scss"; @import "./views/elements/_Tooltip.scss"; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 2cf6276557..4eff5c33e4 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -79,3 +79,7 @@ limitations under the License. .mx_Login_type_dropdown { min-width: 200px; } + +.mx_Login_underlinedServerName { + border-bottom: 1px dashed $accent-color; +} diff --git a/res/css/views/elements/_TextWithTooltip.scss b/res/css/views/elements/_TextWithTooltip.scss new file mode 100644 index 0000000000..5b34e7a3be --- /dev/null +++ b/res/css/views/elements/_TextWithTooltip.scss @@ -0,0 +1,19 @@ +/* +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_TextWithTooltip_tooltip { + display: none; +} \ No newline at end of file diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 5316235fe0..91ed1aa3ae 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -194,9 +194,16 @@ module.exports = React.createClass({ serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + yourMatrixAccountText = _t('Your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index f5b2aec210..80716b766c 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -279,9 +279,16 @@ export default class PasswordLogin extends React.Component { serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + signInToText = _t('Sign in to your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 0eecc6b826..be0142e6c6 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -516,9 +516,16 @@ module.exports = React.createClass({ serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + yourMatrixAccountText = _t('Create your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js new file mode 100644 index 0000000000..757bcc9891 --- /dev/null +++ b/src/components/views/elements/TextWithTooltip.js @@ -0,0 +1,56 @@ +/* + 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'; + +export default class TextWithTooltip extends React.Component { + static propTypes = { + class: PropTypes.string, + tooltip: PropTypes.string.isRequired, + }; + + constructor() { + super(); + + this.state = { + hover: false, + }; + } + + onMouseOver = () => { + this.setState({hover: true}); + }; + + onMouseOut = () => { + this.setState({hover: false}); + }; + + render() { + const Tooltip = sdk.getComponent("elements.Tooltip"); + + return ( + + {this.props.children} + + + ); + } +} \ No newline at end of file diff --git a/src/components/views/elements/Tooltip.js b/src/components/views/elements/Tooltip.js index 1cc82978ed..1d6b54f413 100644 --- a/src/components/views/elements/Tooltip.js +++ b/src/components/views/elements/Tooltip.js @@ -79,6 +79,10 @@ module.exports = React.createClass({ let offset = 0; if (parentBox.height > MIN_TOOLTIP_HEIGHT) { offset = Math.floor((parentBox.height - MIN_TOOLTIP_HEIGHT) / 2); + } else { + // The tooltip is larger than the parent height: figure out what offset + // we need so that we're still centered. + offset = Math.floor(parentBox.height - MIN_TOOLTIP_HEIGHT); } style.top = (parentBox.top - 2) + window.pageYOffset + offset; style.left = 6 + parentBox.right + window.pageXOffset; From 25e3f7888e5453cb25415e2c27ecf09f3c0c4b04 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 18:31:43 -0600 Subject: [PATCH 016/273] newline for the linter --- src/components/views/elements/TextWithTooltip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js index 757bcc9891..61c3a2125a 100644 --- a/src/components/views/elements/TextWithTooltip.js +++ b/src/components/views/elements/TextWithTooltip.js @@ -53,4 +53,4 @@ export default class TextWithTooltip extends React.Component { ); } -} \ No newline at end of file +} From a62f68bd399341ce9810b5d8ff4da3904613dc6c Mon Sep 17 00:00:00 2001 From: Pierre Boyer Date: Tue, 14 May 2019 13:44:01 +0200 Subject: [PATCH 017/273] Hide autocomplete on Enter key press instead of sending message --- src/components/views/rooms/MessageComposerInput.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index e54ddd6787..d8ad9930c8 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -1046,6 +1046,12 @@ export default class MessageComposerInput extends React.Component { return change.insertText('\n'); } + if (this.autocomplete.countCompletions() > 0) { + this.autocomplete.hide(); + ev.preventDefault(); + return true; + } + const editorState = this.state.editorState; const lastBlock = editorState.blocks.last(); From bb163576360e0defc3265f12fc08c5949f97ee69 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 14 May 2019 13:06:56 -0600 Subject: [PATCH 018/273] Flag all generated configs as non-default by default The app is expected to flag a particular config themselves as default. This is primarily intended so that other parts of the app can determine what to do based on whether or not the config is a default config. See https://github.com/vector-im/riot-web/issues/9290 --- src/utils/AutoDiscoveryUtils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js index 318c706136..0850039344 100644 --- a/src/utils/AutoDiscoveryUtils.js +++ b/src/utils/AutoDiscoveryUtils.js @@ -26,6 +26,8 @@ export class ValidatedServerConfig { isUrl: string; identityEnabled: boolean; + + isDefault: boolean; } export default class AutoDiscoveryUtils { @@ -99,6 +101,7 @@ export default class AutoDiscoveryUtils { hsNameIsDifferent: url.hostname !== preferredHomeserverName, isUrl: preferredIdentityUrl, identityEnabled: !SdkConfig.get()['disable_identity_server'], + isDefault: false, }); } } From 34719b9a2e8f4734d9a9367823142ffdb7a32655 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 14 May 2019 13:10:02 -0600 Subject: [PATCH 019/273] Only expose the fallback_hs_url if the homeserver is the default HS See https://github.com/vector-im/riot-web/issues/9290 --- src/components/structures/MatrixChat.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 4198980a17..38f597f673 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -203,9 +203,12 @@ export default React.createClass({ }; }, - // TODO: TravisR - Remove this or put it somewhere else getFallbackHsUrl: function() { - return this.props.config.fallback_hs_url; + if (this.props.serverConfig.isDefault) { + return this.props.config.fallback_hs_url; + } else { + return null; + } }, getServerProperties() { From 9b5830bb080a25781cac66e1807f16a87a9e1ed5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 14 May 2019 15:01:22 -0600 Subject: [PATCH 020/273] Restore use of full mxid login See https://github.com/vector-im/riot-web/issues/9290 --- src/components/structures/auth/Login.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index af9370f2db..3fc7aad50d 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -25,7 +25,7 @@ import sdk from '../../../index'; import Login from '../../../Login'; import SdkConfig from '../../../SdkConfig'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; -import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; +import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // For validating phone numbers without country codes const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/; @@ -235,21 +235,25 @@ module.exports = React.createClass({ this.setState({ username: username }); }, - onUsernameBlur: function(username) { + onUsernameBlur: async function(username) { this.setState({ username: username, + busy: true, // unset later by the result of onServerConfigChange errorText: null, }); - if (username[0] === "@" && false) { // TODO: TravisR - Restore this + if (username[0] === "@") { const serverName = username.split(':').slice(1).join(':'); try { - // we have to append 'https://' to make the URL constructor happy - // otherwise we get things like 'protocol: matrix.org, pathname: 8448' - const url = new URL("https://" + serverName); - this._tryWellKnownDiscovery(url.hostname); + const result = await AutoDiscoveryUtils.validateServerName(serverName); + this.props.onServerConfigChange(result); } catch (e) { console.error("Problem parsing URL or unhandled error doing .well-known discovery:", e); - this.setState({errorText: _t("Failed to perform homeserver discovery")}); + + let message = _t("Failed to perform homeserver discovery"); + if (e.translatedMessage) { + message = e.translatedMessage; + } + this.setState({errorText: message, busy: false}); } } }, From aa361a251b490350ddf563a63943837b763f9a11 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Wed, 10 Apr 2019 16:51:58 +0000 Subject: [PATCH 021/273] Translated using Weblate (Basque) Currently translated at 97.7% (1556 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 0f49dd152e..c4cb32f713 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -1824,5 +1824,6 @@ "Riot failed to get the public room list.": "Riot-ek ezin izan du du gelen zerrenda publikoa eskuratu.", "The homeserver may be unavailable or overloaded.": "Hasiera-zerbitzaria eskuraezin edo kargatuegia egon daiteke.", "You have %(count)s unread notifications in a prior version of this room.|other": "Irakurri gabeko %(count)s jakinarazpen dituzu gela honen aurreko bertsio batean.", - "You have %(count)s unread notifications in a prior version of this room.|one": "Irakurri gabeko %(count)s jakinarazpen duzu gela honen aurreko bertsio batean." + "You have %(count)s unread notifications in a prior version of this room.|one": "Irakurri gabeko %(count)s jakinarazpen duzu gela honen aurreko bertsio batean.", + "Replying With Files": "Fitxategiekin erantzutea" } From 6f393b5d91028c5187cb604180555e9262499e34 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 9 Apr 2019 06:44:03 +0000 Subject: [PATCH 022/273] Translated using Weblate (Bulgarian) Currently translated at 100.0% (1593 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 56 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index aec953b40b..56e4933ae6 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1821,5 +1821,59 @@ "Want more than a community? Get your own server": "Искате повече от общност? Сдобийте се със собствен сървър", "You are logged in to another account": "Влезли сте в друг акаунт", "Thank you for verifying your email! The account you're logged into here (%(sessionUserId)s) appears to be different from the account you've verified an email for (%(verifiedUserId)s). If you would like to log in to %(verifiedUserId2)s, please log out first.": "Благодарим, че потвърждавате имейла си! Акаунтът, с които сте влезли тук (%(sessionUserId)s) изглежда е различен от акаунтът за който потвърждавате имейл адреса (%(verifiedUserId)s). Ако искате да влезете в акаунт %(verifiedUserId2)s, моля първо излезте.", - "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Промяната на паролата Ви, ще анулира всички ключове за шифроване от-край-до-край по всички Ваши устройства, правейки историята на чата нечетима. Настройте резервно копие на ключовете или експортирайте ключовете от друго устройство преди да промените паролата си." + "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Промяната на паролата Ви, ще анулира всички ключове за шифроване от-край-до-край по всички Ваши устройства, правейки историята на чата нечетима. Настройте резервно копие на ключовете или експортирайте ключовете от друго устройство преди да промените паролата си.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Дали използвате 'breadcrumbs' функцията (аватари над списъка със стаи)", + "Replying With Files": "Отговаряне с файлове", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Все още не е възможно да отговорите с файл. Искате ли да качите файла без той да бъде отговор?", + "The file '%(fileName)s' failed to upload.": "Файлът '%(fileName)s' не можа да бъде качен.", + "Room upgrade confirmation": "Потвърждение на обновяването на стаята", + "Upgrading a room can be destructive and isn't always necessary.": "Обновяването на стаята може да бъде деструктивно и не винаги е задължително.", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Обновяването на стаи обикновено се препоръчва за стаи с версии считащи се за нестабилни. Нестабилните версии може да имат бъгове, липсващи функции или проблеми със сигурността.", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Обновяванията на стаи обикновено повлияват само сървърната обработка. Ако имате проблем с Riot, моля съобщете за него със .", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Внимание: Обновяването на стаята няма автоматично да прехвърли членовете в новата версия на стаята. Ще изпратим съобщение в старата стая с връзка към новата - членовете на стаята ще трябва да кликнат на връзката за да влязат в новата стая.", + "Upgrade": "Обнови", + "Adds a custom widget by URL to the room": "Добавя собствено приспособление от URL в стаята", + "Please supply a https:// or http:// widget URL": "Моля, укажете https:// или http:// адрес на приспособление", + "You cannot modify widgets in this room.": "Не можете да модифицирате приспособления в тази стая.", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s премахна покана към %(targetDisplayName)s за присъединяване в стаята.", + "Show recent room avatars above the room list": "Показвай аватари на скоро-използваните стаи над списъка със стаи", + "Enable desktop notifications for this device": "Включи известия на работния плот за това устройство", + "Enable audible notifications for this device": "Включи звукови уведомления за това устройство", + "Upgrade this room to the recommended room version": "Обнови тази стая до препоръчаната версия на стаята", + "This room is running room version , which this homeserver has marked as unstable.": "Тази стая използва версия на стая , която сървърът счита за нестабилна.", + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Обновяването на тази стая ще изключи текущата стая и ще създаде обновена стая със същото име.", + "Failed to revoke invite": "Неуспешно оттегляне на поканата", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Поканата не можа да бъде оттеглена. Или има временен проблем със сървъра, или нямате достатъчно права за да оттеглите поканата.", + "Revoke invite": "Оттегли поканата", + "Invited by %(sender)s": "Поканен от %(sender)s", + "Maximize apps": "Максимизирай приложенията", + "Rotate counter-clockwise": "Завърти обратно на часовниковата стрелка", + "Rotate clockwise": "Завърти по часовниковата стрелка", + "GitHub issue": "GitHub проблем", + "Notes": "Бележки", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "Моля включете допълнителни сведения, които ще помогнат за анализиране на проблема, като например: какво правихте когато възникна проблема, идентификатори на стаи, идентификатори на потребители и т.н.", + "Sign out and remove encryption keys?": "Излизане и премахване на ключовете за шифроване?", + "To help us prevent this in future, please send us logs.": "За да ни помогнете да предотвратим това в бъдеще, моля изпратете логове.", + "Missing session data": "Липсват данни за сесията", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Липсват данни за сесията, като например ключове за шифровани съобщения. За да поправите това, излезте и влезте отново, възстановявайки ключовете от резервно копие.", + "Your browser likely removed this data when running low on disk space.": "Най-вероятно браузърът Ви е премахнал тези данни поради липса на дисково пространство.", + "Upload files (%(current)s of %(total)s)": "Качване на файлове (%(current)s от %(total)s)", + "Upload files": "Качи файлове", + "Upload": "Качи", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Файлът е прекалено голям за да се качи. Максималният допустим размер е %(limit)s, докато този файл е %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Тези файлове са прекалено големи за да се качат. Максималният допустим размер е %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Някои файлове са прекалено големи за да се качат. Максималният допустим размер е %(limit)s.", + "Upload %(count)s other files|other": "Качи %(count)s други файла", + "Upload %(count)s other files|one": "Качи %(count)s друг файл", + "Cancel All": "Откажи всички", + "Upload Error": "Грешка при качване", + "A widget would like to verify your identity": "Приспособление иска да потвърди идентичността Ви", + "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "Приспособлението от адрес %(widgetUrl)s иска да потвърди идентичността Ви. Ако позволите това, приспособлението ще може да потвърди потребителския Ви идентификатор, без да може да извършва действия с него.", + "Remember my selection for this widget": "Запомни избора ми за това приспособление", + "Deny": "Откажи", + "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot не успя да вземе списъка с протоколи от сървъра. Този сървър може да е прекалено стар за да поддържа чужди мрежи.", + "Riot failed to get the public room list.": "Riot не успя да вземе списъка с публични стаи.", + "The homeserver may be unavailable or overloaded.": "Сървърът може да не е наличен или претоварен.", + "You have %(count)s unread notifications in a prior version of this room.|other": "Имате %(count)s непрочетени известия в предишна версия на тази стая.", + "You have %(count)s unread notifications in a prior version of this room.|one": "Имате %(count)s непрочетено известие в предишна версия на тази стая." } From 40d9bc7b348ae6224870883b3d00dfc7d83029b7 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 10 Apr 2019 00:37:51 +0000 Subject: [PATCH 023/273] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1593 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index bb3e1fd84a..a50182149a 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -1862,5 +1862,30 @@ "Riot failed to get the public room list.": "Riot 取得公開聊天室清單失敗。", "The homeserver may be unavailable or overloaded.": "家伺服器似乎不可用或超載。", "You have %(count)s unread notifications in a prior version of this room.|other": "您在此聊天室的先前版本有 %(count)s 個未讀的通知。", - "You have %(count)s unread notifications in a prior version of this room.|one": "您在此聊天室的先前版本有 %(count)s 個未讀的通知。" + "You have %(count)s unread notifications in a prior version of this room.|one": "您在此聊天室的先前版本有 %(count)s 個未讀的通知。", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "不論您是否使用「麵包屑」功能(大頭貼在聊天室清單上)", + "Replying With Files": "以檔案回覆", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "此時無法使用檔案回覆。您想要上傳此檔案而不回覆嗎?", + "The file '%(fileName)s' failed to upload.": "檔案「%(fileName)s」上傳失敗。", + "Show recent room avatars above the room list": "在聊天室清單上顯示聊天室大頭貼", + "Rotate counter-clockwise": "逆時針旋轉", + "Rotate clockwise": "順時針旋轉", + "GitHub issue": "GitHub 議題", + "Notes": "註記", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "如果有其他有助於釐清問題的情境,如您當時正在做什麼,聊天室 ID、使用者 ID 等等,請在這裡加入這些資訊。", + "Sign out and remove encryption keys?": "登出並移除加密金鑰?", + "To help us prevent this in future, please send us logs.": "要協助我們讓這個問題不再發生,請將紀錄檔傳送給我們。", + "Missing session data": "遺失工作階段資料", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "某些工作階段資料遺失了,其中包含加密訊息金鑰。登出再登入並從備份中復原金鑰可以修復這個問題。", + "Your browser likely removed this data when running low on disk space.": "當硬碟空間不足時,您的瀏覽器可能會移除這些資料。", + "Upload files (%(current)s of %(total)s)": "上傳檔案 (%(total)s 中的 %(current)s)", + "Upload files": "上傳檔案", + "Upload": "上傳", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "這個檔案太大了,所以沒辦法上傳。檔案大小限制為 %(limit)s 但這個檔案大小是 %(sizeOfThisFile)s。", + "These files are too large to upload. The file size limit is %(limit)s.": "這些檔案太大了,所以沒辦法上傳。檔案大小限制為 %(limit)s。", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "某些檔案太大了,所以沒辦法上傳。檔案大小限制為 %(limit)s。", + "Upload %(count)s other files|other": "上傳 %(count)s 個其他檔案", + "Upload %(count)s other files|one": "上傳 %(count)s 個其他檔案", + "Cancel All": "取消全部", + "Upload Error": "上傳錯誤" } From 8a94d00edc7b9fb11a601baa773432df599dd8ba Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 8 Apr 2019 16:17:02 +0000 Subject: [PATCH 024/273] Translated using Weblate (Dutch) Currently translated at 100.0% (1593 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nl/ --- src/i18n/strings/nl.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 2b4e50a138..81a5fd0536 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -1771,5 +1771,30 @@ "Riot failed to get the public room list.": "Riot kon de lijst met openbare gesprekken niet verkrijgen.", "The homeserver may be unavailable or overloaded.": "De thuisserver is mogelijk onbereikbaar of overbelast.", "You have %(count)s unread notifications in a prior version of this room.|other": "U heeft %(count)s ongelezen meldingen in een voorgaande versie van dit gesprek.", - "You have %(count)s unread notifications in a prior version of this room.|one": "U heeft %(count)s ongelezen melding in een voorgaande versie van dit gesprek." + "You have %(count)s unread notifications in a prior version of this room.|one": "U heeft %(count)s ongelezen melding in een voorgaande versie van dit gesprek.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Of u de 'broodkruimels'-functie al dan niet gebruikt (avatars boven de gesprekslijst)", + "Replying With Files": "Beantwoorden met bestanden", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Momenteel is het niet mogelijk om met een bestand te antwoorden. Wilt u dit bestand uploaden zonder te antwoorden?", + "The file '%(fileName)s' failed to upload.": "Het bestand ‘%(fileName)s’ kon niet geüpload worden.", + "Show recent room avatars above the room list": "Recente gespreksavatars weergeven boven de gesprekslijst", + "Rotate counter-clockwise": "Tegen de klok in draaien", + "Rotate clockwise": "Met de klok mee draaien", + "GitHub issue": "GitHub-melding", + "Notes": "Opmerkingen", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "Indien er extra context zou kunnen helpen om het probleem te analyseren, zoals wat u aan het doen was, relevante gespreks-ID’s, gebruikers-ID’s, enz., gelieve deze informatie dan hier mee te geven.", + "Sign out and remove encryption keys?": "Afmelden en versleutelingssleutels verwijderen?", + "To help us prevent this in future, please send us logs.": "Gelieve ons logboeken te sturen om dit in de toekomst te helpen voorkomen.", + "Missing session data": "Sessiegegevens ontbreken", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Sommige sessiegegevens, inclusief sleutels voor versleutelde berichten, ontbreken. Meld u af en weer aan om dit op te lossen, en herstel de sleutels uit de back-up.", + "Your browser likely removed this data when running low on disk space.": "Uw browser heeft deze gegevens mogelijk verwijderd toen de beschikbare opslagruimte vol was.", + "Upload files (%(current)s of %(total)s)": "Bestanden worden geüpload (%(current)s van %(total)s)", + "Upload files": "Bestanden uploaden", + "Upload": "Uploaden", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Dit bestand is te groot om te uploaden. De bestandsgroottelimiet is %(limit)s, maar dit bestand is %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Deze bestanden zijn te groot om te uploaden. De bestandsgroottelimiet is %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Sommige bestanden zijn te groot om te uploaden. De bestandsgroottelimiet is %(limit)s.", + "Upload %(count)s other files|other": "%(count)s overige bestanden uploaden", + "Upload %(count)s other files|one": "%(count)s overig bestand uploaden", + "Cancel All": "Alles annuleren", + "Upload Error": "Uploadfout" } From 129cb8ea29c8c5d2fd62155695cb96f4272f710a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Mon, 8 Apr 2019 12:44:19 +0000 Subject: [PATCH 025/273] Translated using Weblate (French) Currently translated at 99.9% (1591 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 8afaeef2e6..4da12b701d 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1867,5 +1867,30 @@ "Riot failed to get the public room list.": "Riot n’a pas pu récupérer la liste des salons publics.", "The homeserver may be unavailable or overloaded.": "Le serveur d’accueil est peut-être indisponible ou surchargé.", "You have %(count)s unread notifications in a prior version of this room.|other": "Vous avez %(count)s notifications non lues dans une version précédente de ce salon.", - "You have %(count)s unread notifications in a prior version of this room.|one": "Vous avez %(count)s notification non lue dans une version précédente de ce salon." + "You have %(count)s unread notifications in a prior version of this room.|one": "Vous avez %(count)s notification non lue dans une version précédente de ce salon.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Si vous utilisez ou non la fonction « fil d’ariane » (les avatars au-dessus de la liste des salons)", + "Replying With Files": "Répondre avec des fichiers", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Pour le moment, il n’est pas possible de répondre avec un fichier. Souhaitez-vous envoyer ce fichier sans répondre ?", + "The file '%(fileName)s' failed to upload.": "Le fichier « %(fileName)s » n’a pas pu être envoyé.", + "Show recent room avatars above the room list": "Afficher les avatars des salons récents au-dessus de la liste des salons", + "Rotate counter-clockwise": "Pivoter dans le sens inverse des aiguilles d’une montre", + "Rotate clockwise": "Pivoter dans le sens des aiguilles d’une montre", + "GitHub issue": "Rapport GitHub", + "Notes": "Notes", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "S’il y a des informations supplémentaires qui pourraient nous aider à analyser le problème, comme ce que vous faisiez, l’identifiant du salon ou des utilisateurs etc, veuillez les préciser ici.", + "Sign out and remove encryption keys?": "Se déconnecter et supprimer les clés de chiffrement ?", + "To help us prevent this in future, please send us logs.": "Pour nous aider à éviter cela dans le futur, veuillez nous envoyer les journaux.", + "Missing session data": "Données de la session manquantes", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Des données de la session, dont les clés des messages chiffrés, sont manquantes. Déconnectez-vous et reconnectez-vous pour régler ce problème, en restaurant les clés depuis la sauvegarde.", + "Your browser likely removed this data when running low on disk space.": "Votre navigateur a sûrement supprimé ces données car il restait peu d’espace sur le disque.", + "Upload files (%(current)s of %(total)s)": "Envoi des fichiers (%(current)s sur %(total)s)", + "Upload files": "Envoyer les fichiers", + "Upload": "Envoyer", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Le fichier est trop lourd pour être envoyé. La taille limite est de %(limit)s mais la taille de ce fichier est de %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Ces fichiers sont trop lourds pour être envoyés. La taille limite des fichiers est de %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Certains fichiers sont trop lourds pour être envoyés. La taille limite des fichiers est de %(limit)s.", + "Upload %(count)s other files|other": "Envoyer %(count)s autres fichiers", + "Upload %(count)s other files|one": "Envoyer %(count)s autre fichier", + "Cancel All": "Tout annuler", + "Upload Error": "Erreur d’envoi" } From b55b41257a26a894488aff9aa78cecd5d46595fc Mon Sep 17 00:00:00 2001 From: natowi Date: Mon, 8 Apr 2019 13:46:15 +0000 Subject: [PATCH 026/273] Translated using Weblate (German) Currently translated at 92.7% (1477 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 3cfdb9c733..a21dae887a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1428,7 +1428,7 @@ "General failure": "Allgemeiner Fehler", "Failed to perform homeserver discovery": "Fehler beim Aufspüren des Heimservers", "Unknown failure discovering homeserver": "Unbekannter Fehler beim Aufspüren des Heimservers", - "Great! This passphrase looks strong enough.": "Gut! Diese Passphrase sieht start genug aus.", + "Great! This passphrase looks strong enough.": "Gut! Diese Passphrase sieht stark genug aus.", "Secure your encrypted message history with a Recovery Passphrase.": "Sichere deine sichere Nachrichtenhistorie mit einer Wiederherstellungspassphrase.", "If you don't want encrypted message history to be available on other devices, .": "Wenn du deine verschlüsselte Nachrichtenhistorie nicht auf anderen Geräten verfügbar haben möchtest, .", "Or, if you don't want to create a Recovery Passphrase, skip this step and .": "Oder, wenn du keine Wiederherstellungspassphrase erzeugen möchtest, überspringe diesen Schritt und .", @@ -1807,5 +1807,6 @@ "Thank you for verifying your email! The account you're logged into here (%(sessionUserId)s) appears to be different from the account you've verified an email for (%(verifiedUserId)s). If you would like to log in to %(verifiedUserId2)s, please log out first.": "Danke für das Verifizieren deiner E-Mail! Das Konto, mit dem du angemeldet bist (%(sessionUserId)s) scheint ein anderes zu sein als das wofür die die E-Mail verifizierst (%(verifiedUserId)s). Wenn du dich als %(verifiedUserId2)s anmelden willst, melde dich zuerst ab.", "Could not load user profile": "Konnte Nutzerprofil nicht laden", "Your Matrix account": "Dein Matrixkonto", - "Your Matrix account on %(serverName)s": "Dein Matrixkonto auf %(serverName)s" + "Your Matrix account on %(serverName)s": "Dein Matrixkonto auf %(serverName)s", + "Show recent room avatars above the room list": "Zeige die letzten Avatare über der Raumliste an (neu laden um Änderungen zu übernehmen)" } From 6f63df5694403c35ab375fc02036ef2c604a4b15 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 8 Apr 2019 19:26:46 +0000 Subject: [PATCH 027/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1593 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index e7f0164779..c83431ac44 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1867,5 +1867,30 @@ "Riot failed to get the public room list.": "Riotnak nem sikerült beszereznie a nyilvános szoba listát.", "The homeserver may be unavailable or overloaded.": "A Matrix szerver elérhetetlen vagy túlterhelt.", "You have %(count)s unread notifications in a prior version of this room.|other": "%(count)s olvasatlan értesítésed van a régi verziójú szobában.", - "You have %(count)s unread notifications in a prior version of this room.|one": "%(count)s olvasatlan értesítésed van a régi verziójú szobában." + "You have %(count)s unread notifications in a prior version of this room.|one": "%(count)s olvasatlan értesítésed van a régi verziójú szobában.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Használsz vagy nem „morzsákat” (profilképek a szobalista felett)", + "Replying With Files": "Válasz fájlokkal", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Egyenlőre nem lehet fájlal válaszolni. Szeretnéd feltölteni a fájlt úgy, hogy az nem egy válasz lesz?", + "The file '%(fileName)s' failed to upload.": "A %(fileName)s fájlt nem sikerült feltölteni.", + "Show recent room avatars above the room list": "A legfrissebb szoba profilképét mutassa a szoba lista felett", + "Rotate counter-clockwise": "Óramutató járásával ellentétesen fordít", + "Rotate clockwise": "Óramutató járásával megegyező irányba fordít", + "GitHub issue": "GitHub hibajegy", + "Notes": "Megjegyzések", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "Ha a hiba felderítésében további adat is segítséget adhat, mint az, hogy mit csináltál éppen, mi a szoba-, felhasználó azonosítója, stb... itt add meg.", + "Sign out and remove encryption keys?": "Kilépés és a titkosítási kulcsok törlése?", + "To help us prevent this in future, please send us logs.": "Segíts abban, hogy ez később ne fordulhasson elő, kérlek küld el a naplókat.", + "Missing session data": "A kapcsolati adat hiányzik", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Néhány kapcsolati adat hiányzik, beleértve a titkosított üzenetek kulcsait. Lépj ki és jelentkezz vissza a hiba javításához és állítsd vissza mentésből a kulcsokat.", + "Your browser likely removed this data when running low on disk space.": "A böngésző valószínűleg törölte ezeket az adatokat amikor lecsökkent a szabad lemezterület.", + "Upload files (%(current)s of %(total)s)": "Fájlok feltöltése (%(current)s / %(total)s)", + "Upload files": "Fájlok feltöltése", + "Upload": "Feltöltés", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Ez a fájl túl nagy, hogy fel lehessen tölteni. A fájl méret korlát %(limit)s de a fájl %(sizeOfThisFile)s méretű.", + "These files are too large to upload. The file size limit is %(limit)s.": "A fájl túl nagy a feltöltéshez. A fájlméret korlát %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Néhány fájl túl nagy, hogy fel lehessen tölteni. A fájlméret korlát %(limit)s.", + "Upload %(count)s other files|other": "Feltölt %(count)s másik fájlt", + "Upload %(count)s other files|one": "Feltölt %(count)s másik fájlt", + "Cancel All": "Mindent megszakít", + "Upload Error": "Feltöltési hiba" } From e4b4da4f8d0a6c975bb38ab94ba9658753efacb1 Mon Sep 17 00:00:00 2001 From: Kenneth Larsson Date: Mon, 8 Apr 2019 19:50:53 +0000 Subject: [PATCH 028/273] Translated using Weblate (Swedish) Currently translated at 84.8% (1351 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sv/ --- src/i18n/strings/sv.json | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/sv.json b/src/i18n/strings/sv.json index cb60d31e48..15673d23e4 100644 --- a/src/i18n/strings/sv.json +++ b/src/i18n/strings/sv.json @@ -380,7 +380,7 @@ "Online": "Online", "Unnamed room": "Namnlöst rum", "World readable": "Alla kan läsa", - "Guests can join": "Gäster kan bli medlem i rummet", + "Guests can join": "Gäster kan gå med i rummet", "No rooms to show": "Inga fler rum att visa", "This phone number is already in use": "Detta telefonnummer används redan", "The version of Riot.im": "Versionen av Riot.im", @@ -1607,5 +1607,59 @@ "Unable to load backup status": "Det går inte att ladda backupstatus", "Guest": "Gäst", "Could not load user profile": "Kunde inte ladda användarprofil", - "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Om du ändrar ditt lösenord återställs eventuella krypteringsnycklar på alla dina enheter, vilket gör att krypterad chatthistorik inte kan läsas. Aktivera nyckelsäkerhetskopiering eller exportera dina rumsnycklar från en annan enhet innan du återställer ditt lösenord." + "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Om du ändrar ditt lösenord återställs eventuella krypteringsnycklar på alla dina enheter, vilket gör att krypterad chatthistorik inte kan läsas. Aktivera nyckelsäkerhetskopiering eller exportera dina rumsnycklar från en annan enhet innan du återställer ditt lösenord.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Om du använder 'breadcrumbs' eller inte (avatarer ovanför rumslistan)", + "Replying With Files": "Svarar med filer", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Just nu är det inte möjligt att svara med en fil. Vill du ladda upp filen utan att svara?", + "The file '%(fileName)s' failed to upload.": "Filen '%(fileName)s' kunde inte laddas upp.", + "Room upgrade confirmation": "Bekräfta rumsuppgradering", + "Upgrading a room can be destructive and isn't always necessary.": "Uppgradering av ett rum kan vara destruktivt och är inte alltid nödvändigt.", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Rumsuppgraderingar rekommenderas vanligtvis när en rumversion anses vara instabil. Instabila rumsversioner kan ha fel, sakna funktioner eller ha säkerhetsproblem.", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "", + "Upgrade": "Uppgradera", + "Close button should minimize window to tray": "Stängknappen ska minimera fönstret till systemfältet", + "Composer": "Meddelandefält", + "Key backup": "Nyckelsäkerhetskopiering", + "Never lose encrypted messages": "Förlora aldrig krypterade meddelanden", + "Securely back up your keys to avoid losing them. Learn more.": "Säkerhetskopiera dina nycklar på ett säkert sätt för att undvika att förlora dem. Läs mer.", + "Failed to load group members": "Det gick inte att ladda gruppmedlemmar", + "Maximize apps": "Maximera appar", + "Join": "Gå med", + "Rotate counter-clockwise": "Rotera moturs", + "Rotate clockwise": "Rotera medurs", + "Power level": "Behörighetsnivå", + "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Det gick inte att hitta profiler för de Matrix-IDn som anges nedan - vill du bjuda in dem ändå?", + "GitHub issue": "GitHub-ärende", + "Notes": "Noteringar", + "You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "Du har tidigare använt Riot på %(host)s med lazy loading av medlemmar aktiverat. I den här versionen är lazy loading inaktiverat. Eftersom den lokala cachen inte är kompatibel mellan dessa två inställningar behöver Riot synkronisera om ditt konto.", + "If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Om den andra versionen av Riot fortfarande är öppen i en annan flik, stäng den eftersom användning av Riot på samma värd med både lazy loading aktiverad och inaktiverad samtidigt kommer att orsaka problem.", + "Incompatible local cache": "Inkompatibel lokal cache", + "Clear cache and resync": "Töm cache och synkronisera om", + "Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot använder nu 3-5 gånger mindre minne, genom att bara ladda information om andra användare när det behövs. Vänta medan vi återsynkroniserar med servern!", + "I don't want my encrypted messages": "Jag vill inte ha mina krypterade meddelanden", + "Manually export keys": "Exportera nycklar manuellt", + "You'll lose access to your encrypted messages": "Du kommer att förlora åtkomst till dina krypterade meddelanden", + "Are you sure you want to sign out?": "Är du säker på att du vill logga ut?", + "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Om du stöter på några fel eller har feedback du vill dela, vänligen meddela oss på GitHub.", + "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "För att undvika dubbla ärenden, vänligen granska befintliga ärenden först (och lägg till +1) eller skapa ett nytt ärende om du inte hittar det.", + "Report bugs & give feedback": "Rapportera fel och ge feedback", + "Go back": "Gå tillbaka", + "Room Settings - %(roomName)s": "Rumsinställningar - %(roomName)s", + "Sign out and remove encryption keys?": "Logga ut och ta bort krypteringsnycklar?", + "A username can only contain lower case letters, numbers and '=_-./'": "Ett användarnamn får endast innehålla små bokstäver, siffror och '=_-./'", + "Checking...": "Kontrollerar...", + "To help us prevent this in future, please send us logs.": "För att hjälpa oss att förhindra detta i framtiden, vänligen skicka oss loggar.", + "Missing session data": "Sessionsdata saknas", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Vissa sessionsdata, inklusive krypteringsnycklar för meddelanden, saknas. Logga ut och logga in för att åtgärda detta genom återställning av nycklarna från säkerhetskopia.", + "Your browser likely removed this data when running low on disk space.": "Din webbläsare har troligen tagit bort dessa data när det blev ont om diskutrymme.", + "Upload files (%(current)s of %(total)s)": "Ladda upp filer (%(current)s av %(total)s)", + "Upload files": "Ladda upp filer", + "Upload": "Ladda upp", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Den här filen är för stor för att ladda upp. Filstorleksgränsen är %(limit)s men den här filen är %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Dessa filer är för stora för att laddas upp. Filstorleksgränsen är %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Vissa filer är för stora för att laddas upp. Filstorleksgränsen är %(limit)s.", + "Upload %(count)s other files|other": "Ladda upp %(count)s andra filer", + "Upload %(count)s other files|one": "Ladda upp %(count)s annan fil", + "Cancel All": "Avbryt alla", + "Upload Error": "Uppladdningsfel" } From 52c4d7b6b09ea433f54cea2f1ca5875e992cf14d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 15 May 2019 15:53:02 +0100 Subject: [PATCH 029/273] fixup edit icon --- res/img/edit.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/img/edit.svg b/res/img/edit.svg index 95bd44f606..9674b31690 100644 --- a/res/img/edit.svg +++ b/res/img/edit.svg @@ -1 +1 @@ - \ No newline at end of file + From b081a3156f86d2f3b952edaa1ddcb16f00c5cde5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 15 May 2019 15:53:49 +0100 Subject: [PATCH 030/273] dont show edit button for messages that are not your own --- src/components/views/messages/MessageActionBar.js | 14 +++++++------- src/utils/EventUtils.js | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index fe6c22ab1e..84474710cd 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -23,7 +23,7 @@ import dis from '../../../dispatcher'; import Modal from '../../../Modal'; import { createMenu } from '../../structures/ContextualMenu'; import SettingsStore from '../../../settings/SettingsStore'; -import { isContentActionable } from '../../../utils/EventUtils'; +import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; export default class MessageActionBar extends React.PureComponent { static propTypes = { @@ -148,12 +148,12 @@ export default class MessageActionBar extends React.PureComponent { title={_t("Reply")} onClick={this.onReplyClick} />; - if (this.isEditingEnabled()) { - editButton = ; - } + } + if (this.isEditingEnabled() && canEditContent(this.props.mxEvent)) { + editButton = ; } return
diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.js index 911257f95c..ac415ca6de 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -15,6 +15,7 @@ limitations under the License. */ import { EventStatus } from 'matrix-js-sdk'; +import MatrixClientPeg from '../MatrixClientPeg'; /** * Returns whether an event should allow actions like reply, reactions, edit, etc. @@ -43,3 +44,8 @@ export function isContentActionable(mxEvent) { return false; } + +export function canEditContent(mxEvent) { + return isContentActionable(mxEvent) && + mxEvent.getSender() === MatrixClientPeg.get().getUserId(); +} From 6366371c0db064860024aff6cfa2e1e229de9d0b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 15 May 2019 15:54:05 +0100 Subject: [PATCH 031/273] add * to fallback messages for edits --- src/components/views/elements/MessageEditor.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index a2005feace..20d6dc6f66 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -110,9 +110,15 @@ export default class MessageEditor extends React.Component { "msgtype": "m.text", "body": textSerialize(this.model), }; + const contentBody = { + msgtype: newContent.msgtype, + body: ` * ${newContent.body}`, + }; if (requiresHtml(this.model)) { newContent.format = "org.matrix.custom.html"; newContent.formatted_body = htmlSerialize(this.model); + contentBody.format = newContent.format; + contentBody.formatted_body = ` * ${newContent.formatted_body}`; } const content = Object.assign({ "m.new_content": newContent, @@ -120,7 +126,7 @@ export default class MessageEditor extends React.Component { "rel_type": "m.replace", "event_id": this.props.event.getId(), }, - }, newContent); + }, contentBody); const roomId = this.props.event.getRoomId(); this.context.matrixClient.sendMessage(roomId, content); From d73f547f556d98013d76fefcc95069f321015e4c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 15 May 2019 15:55:03 +0100 Subject: [PATCH 032/273] reapply pills, link preview, code highlighting, ... after edit --- src/components/views/messages/TextualBody.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index deb3c5cc0f..34769c060f 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -88,7 +88,10 @@ module.exports = React.createClass({ componentDidMount: function() { this._unmounted = false; + this._applyFormatting(); + }, + _applyFormatting() { // pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer // are still sent as plaintext URLs. If these are ever pillified in the composer, // we should be pillify them here by doing the linkifying BEFORE the pillifying. @@ -123,7 +126,11 @@ module.exports = React.createClass({ } }, - componentDidUpdate: function() { + componentDidUpdate: function(prevProps) { + const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId; + if (messageWasEdited) { + this._applyFormatting(); + } this.calculateUrlPreview(); }, From 085f2d199de8468ae363428872d0a34101f695f0 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 15 May 2019 17:17:35 +0100 Subject: [PATCH 033/273] focus editor after clicking edit --- src/components/views/elements/MessageEditor.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index 20d6dc6f66..b42923954b 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -144,6 +144,13 @@ export default class MessageEditor extends React.Component { componentDidMount() { this._updateEditorState(); + const sel = document.getSelection(); + const range = document.createRange(); + range.selectNodeContents(this._editorRef); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + this._editorRef.focus(); } render() { From 6e3b06f3646b83d4e271e51ca894c718474d8c27 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 15 May 2019 13:55:50 -0600 Subject: [PATCH 034/273] Human de-linting --- src/components/structures/auth/ForgotPassword.js | 3 ++- src/components/views/auth/PasswordLogin.js | 5 +++-- src/components/views/auth/RegistrationForm.js | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 91ed1aa3ae..42ca23256c 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -200,7 +200,8 @@ module.exports = React.createClass({ 'underlinedServerName': () => { return ; }, diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index 80716b766c..825bffdc84 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -284,8 +284,9 @@ export default class PasswordLogin extends React.Component { signInToText = _t('Sign in to your Matrix account on ', {}, { 'underlinedServerName': () => { return ; }, diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index be0142e6c6..b1af6ea42c 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -522,7 +522,8 @@ module.exports = React.createClass({ 'underlinedServerName': () => { return ; }, From bfd37d808942834524348326cffbbcb457c3eaa1 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 15 May 2019 22:05:54 -0500 Subject: [PATCH 035/273] Add tooltips to rotate and close buttons in ImageView (#9686) Signed-off-by: Aaron Raimist --- src/components/views/elements/ImageView.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 2adc0df57f..ae481f0659 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -195,13 +195,13 @@ export default class ImageView extends React.Component {
- + { - + { - + {
From c38b5cabb1a3d8786309389c4b653573f7b01456 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 15 May 2019 22:11:47 -0500 Subject: [PATCH 036/273] Allow the strings to be translated Signed-off-by: Aaron Raimist --- src/components/views/elements/ImageView.js | 6 +++--- src/i18n/strings/en_EN.json | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index ae481f0659..9a47bce553 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -195,13 +195,13 @@ export default class ImageView extends React.Component {
- + { - + { - + {
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e407d92630..88d78b1bde 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -985,7 +985,9 @@ "Communities": "Communities", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", + "Rotate Left": "Rotate Left", "Rotate counter-clockwise": "Rotate counter-clockwise", + "Rotate Right": "Rotate Right", "Rotate clockwise": "Rotate clockwise", "Download this file": "Download this file", "Integrations Error": "Integrations Error", From 33885cb864512e12df55d5dbb66a02565cfe9d4b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 15 May 2019 13:46:32 +0100 Subject: [PATCH 037/273] Use `getRelation` helper Use the `getRelation` helper to ensure we always read relation info from the wire content as required in E2E rooms. --- src/components/views/elements/ReplyThread.js | 5 +++++ src/components/views/messages/ReactionDimension.js | 2 +- src/components/views/messages/ReactionsRow.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index f3cd6e144d..ab7b1abb1c 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -63,6 +63,11 @@ export default class ReplyThread extends React.Component { static getParentEventId(ev) { if (!ev || ev.isRedacted()) return; + // XXX: For newer relations (annotations, replacements, etc.), we now + // have a `getRelation` helper on the event, and you might assume it + // could be used here for replies as well... However, the helper + // currently assumes the relation has a `rel_type`, which older replies + // do not, so this block is left as-is for now. const mRelatesTo = ev.getWireContent()['m.relates_to']; if (mRelatesTo && mRelatesTo['m.in_reply_to']) { const mInReplyTo = mRelatesTo['m.in_reply_to']; diff --git a/src/components/views/messages/ReactionDimension.js b/src/components/views/messages/ReactionDimension.js index a0cf5a86ec..aa3813c30d 100644 --- a/src/components/views/messages/ReactionDimension.js +++ b/src/components/views/messages/ReactionDimension.js @@ -82,7 +82,7 @@ export default class ReactionDimension extends React.PureComponent { if (mxEvent.isRedacted()) { return false; } - return mxEvent.getContent()["m.relates_to"].key === option; + return mxEvent.getRelation().key === option; }); if (!reactionForOption) { continue; diff --git a/src/components/views/messages/ReactionsRow.js b/src/components/views/messages/ReactionsRow.js index ffb81e1a38..f4bd9781bc 100644 --- a/src/components/views/messages/ReactionsRow.js +++ b/src/components/views/messages/ReactionsRow.js @@ -101,7 +101,7 @@ export default class ReactionsRow extends React.PureComponent { if (mxEvent.isRedacted()) { return false; } - return mxEvent.getContent()["m.relates_to"].key === content; + return mxEvent.getRelation().key === content; }); return Date: Thu, 16 May 2019 07:47:13 +0000 Subject: [PATCH 038/273] Translated using Weblate (Hindi) Currently translated at 45.3% (722 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hi/ --- src/i18n/strings/hi.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hi.json b/src/i18n/strings/hi.json index 162ddf08a4..7ff6c6a7cf 100644 --- a/src/i18n/strings/hi.json +++ b/src/i18n/strings/hi.json @@ -772,5 +772,25 @@ "To ban users, you must be a": "उपयोगकर्ताओं को प्रतिबंधित करने के लिए, आपको होना चाहिए", "To remove other users' messages, you must be a": "अन्य उपयोगकर्ताओं के संदेशों को हटाने के लिए, आपको होना चाहिए", "To notify everyone in the room, you must be a": "कमरे में सभी को सूचित करने के लिए, आपको होना चाहिए", - "No users have specific privileges in this room": "इस कमरे में किसी भी उपयोगकर्ता के विशेष विशेषाधिकार नहीं हैं" + "No users have specific privileges in this room": "इस कमरे में किसी भी उपयोगकर्ता के विशेष विशेषाधिकार नहीं हैं", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "आप 'ब्रेडक्रंब' सुविधा का उपयोग कर रहे हैं या नहीं (कमरे की सूची के ऊपर अवतार)", + "Replying With Files": "फाइलों के साथ उत्तर", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "इस समय किसी फ़ाइल के साथ उत्तर देना संभव नहीं है। क्या आप इस फ़ाइल को बिना उत्तर दिए अपलोड करना चाहेंगे?", + "The file '%(fileName)s' failed to upload.": "फ़ाइल '%(fileName)s' अपलोड करने में विफल रही।", + "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "एक सादे पाठ संदेश के लिए ¯\\_(ツ)_/¯ प्रस्तुत करता है", + "Room upgrade confirmation": "रूम के उन्नयन की पुष्टि", + "Upgrading a room can be destructive and isn't always necessary.": "एक कमरे को अपग्रेड करना विनाशकारी हो सकता है और हमेशा आवश्यक नहीं होता है।", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "एक कमरे के संस्करण को अस्थिर माना जाता है, तो आमतौर पर कमरे के उन्नयन की सिफारिश की जाती है। अस्थिर कमरे के संस्करणों में बग, लापता विशेषताएं या सुरक्षा कमजोरियां हो सकती हैं।", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "रूम का उन्नयन आमतौर पर केवल रूम के सर्वर-साइड को प्रभावित करता है। यदि आपको अपने रायट क्लाइंट के साथ समस्या हो रही है, तो कृपया के साथ एक समस्या दर्ज करें।", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "चेतावनी: किसी कमरे को अपग्रेड करना कमरे के सदस्यों को कमरे के नए संस्करण में स्वचालित रूप से माइग्रेट नहीं करना है। हम कमरे के पुराने संस्करण में नए कमरे के लिए एक लिंक पोस्ट करेंगे। नए कमरे में शामिल होने के लिए कमरे के सदस्यों को इस लिंक पर क्लिक करना होगा।", + "Upgrade": "अपग्रेड", + "Adds a custom widget by URL to the room": "रूम में URL द्वारा एक कस्टम विजेट जोड़ता है", + "Please supply a https:// or http:// widget URL": "कृपया एक https:// या http:// विजेट URL की आपूर्ति करें", + "You cannot modify widgets in this room.": "आप इस रूम में विजेट्स को संशोधित नहीं कर सकते।", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s ने कमरे में शामिल होने के लिए %(targetDisplayName)s के निमंत्रण को रद्द कर दिया।", + "User %(userId)s is already in the room": "उपयोगकर्ता %(userId)s पहले से ही रूम में है", + "The user must be unbanned before they can be invited.": "उपयोगकर्ता को आमंत्रित करने से पहले उन्हें प्रतिबंधित किया जाना चाहिए।", + "Show recent room avatars above the room list": "रूम की सूची के ऊपर हाल के अवतारों को दिखाएं", + "Enable desktop notifications for this device": "इस उपकरण के लिए डेस्कटॉप सूचनाएं सक्षम करें", + "Enable audible notifications for this device": "इस उपकरण के लिए श्रव्य सूचनाएँ सक्षम करें" } From 7f7e7221c5b2567b8579832ba5f2958f1f874ae1 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Wed, 15 May 2019 19:29:45 +0000 Subject: [PATCH 039/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1593 of 1593 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index c83431ac44..d0a4560dc0 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -961,7 +961,7 @@ "Failed to add tag %(tagName)s to room": "Nem sikerült hozzáadni a szobához ezt: %(tagName)s", "Clear filter": "Szűrő törlése", "Disable Community Filter Panel": "Közösség keresési panel tiltása", - "Did you know: you can use communities to filter your Riot.im experience!": "Tudtad, hogy a Riot.im élmény fokozásához használhatsz közösségeket?", + "Did you know: you can use communities to filter your Riot.im experience!": "Tudtad, hogy a Riot.im élmény fokozásához használhatsz közösségeket!", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "A szűrő beállításához húzd a közösség avatarját a szűrő panel fölé a képernyő bal szélén. A szűrő panelen az avatarra kattintva bármikor leszűrheted azokat a szobákat és embereket akik a megadott közösséghez tartoznak.", "Your key share request has been sent - please check your other devices for key share requests.": "A kulcs megosztási kérést elküldtük - ellenőrizd a többi eszközödön a kulcs megosztási kéréseket.", "Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "A kulcs megosztási kérelem automatikusan el lett küldve a többi eszközödre. Ha elutasítottad vagy törölted a kérést a másik eszközön ide kattintva újra kérheted a kulcsokat.", @@ -1175,8 +1175,8 @@ "Please forget all messages I have sent when my account is deactivated (Warning: this will cause future users to see an incomplete view of conversations)": "Kérlek töröld az összes általam küldött üzenetet amikor a fiókomat felfüggesztem (Figyelem: ez azt eredményezheti, hogy a jövőbeni felhasználók csak részleges beszélgetést látnak majd)", "e.g. %(exampleValue)s": "pl. %(exampleValue)s", "Reload widget": "Kisalkalmazás újratöltése", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Kérlek segíts javítani a Riot.im-et azzal, hogy anonim felhasználási adatokat küldesz. Ez szütit (cookie) fog használni (lásd a sütire vonatkozó szabályozásunkat).", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Kérlek segíts javítani a Riot.im-et azzal, hogy anonim felhasználási adatokat küldesz. Ez szütit (cookie) fog használni.", + "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Kérlek segíts javítani a Riot.im-et azzal, hogy anonim felhasználási adatokat küldesz. Ez sütit (cookie) fog használni (lásd a sütire vonatkozó szabályozásunkat).", + "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Kérlek segíts javítani a Riot.im-et azzal, hogy anonim felhasználási adatokat küldesz. Ez sütit (cookie) fog használni.", "Yes, I want to help!": "Igen, segítek!", "Can't leave Server Notices room": "Nem lehet elhagyni a Szerver Üzenetek szobát", "This room is used for important messages from the Homeserver, so you cannot leave it.": "Ez a szoba fontos szerverüzenetek közlésére jött létre, nem tudsz kilépni belőle.", From 2e7e71556b0df0ec04e6a6ad06ae4d8e9253ea0a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 16 May 2019 12:45:41 +0100 Subject: [PATCH 040/273] Annotations by sender is now a Set --- src/components/views/messages/ReactionDimension.js | 2 +- src/components/views/messages/ReactionsRow.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/ReactionDimension.js b/src/components/views/messages/ReactionDimension.js index aa3813c30d..4ec50e3036 100644 --- a/src/components/views/messages/ReactionDimension.js +++ b/src/components/views/messages/ReactionDimension.js @@ -107,7 +107,7 @@ export default class ReactionDimension extends React.PureComponent { return null; } const userId = MatrixClientPeg.get().getUserId(); - return reactions.getAnnotationsBySender()[userId]; + return [...reactions.getAnnotationsBySender()[userId].values()]; } onOptionClick = (ev) => { diff --git a/src/components/views/messages/ReactionsRow.js b/src/components/views/messages/ReactionsRow.js index f4bd9781bc..780d38ceee 100644 --- a/src/components/views/messages/ReactionsRow.js +++ b/src/components/views/messages/ReactionsRow.js @@ -80,7 +80,7 @@ export default class ReactionsRow extends React.PureComponent { return null; } const userId = MatrixClientPeg.get().getUserId(); - return reactions.getAnnotationsBySender()[userId]; + return [...reactions.getAnnotationsBySender()[userId].values()]; } render() { From e942939be9dd8a60f10d450f1237b904bdcce103 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 16 May 2019 13:03:05 +0100 Subject: [PATCH 041/273] Listen for removed relations The JS SDK has learned how to remove relations when cancelled, so we should also listen for those as well. Part of https://github.com/vector-im/riot-web/issues/9731 --- src/components/views/messages/ReactionDimension.js | 6 ++++++ src/components/views/messages/ReactionsRow.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/components/views/messages/ReactionDimension.js b/src/components/views/messages/ReactionDimension.js index 4ec50e3036..0c91d0ac99 100644 --- a/src/components/views/messages/ReactionDimension.js +++ b/src/components/views/messages/ReactionDimension.js @@ -37,6 +37,7 @@ export default class ReactionDimension extends React.PureComponent { if (props.reactions) { props.reactions.on("Relations.add", this.onReactionsChange); + props.reactions.on("Relations.remove", this.onReactionsChange); props.reactions.on("Relations.redaction", this.onReactionsChange); } } @@ -44,6 +45,7 @@ export default class ReactionDimension extends React.PureComponent { componentDidUpdate(prevProps) { if (prevProps.reactions !== this.props.reactions) { this.props.reactions.on("Relations.add", this.onReactionsChange); + this.props.reactions.on("Relations.remove", this.onReactionsChange); this.props.reactions.on("Relations.redaction", this.onReactionsChange); this.onReactionsChange(); } @@ -55,6 +57,10 @@ export default class ReactionDimension extends React.PureComponent { "Relations.add", this.onReactionsChange, ); + this.props.reactions.removeListener( + "Relations.remove", + this.onReactionsChange, + ); this.props.reactions.removeListener( "Relations.redaction", this.onReactionsChange, diff --git a/src/components/views/messages/ReactionsRow.js b/src/components/views/messages/ReactionsRow.js index 780d38ceee..cd3ccb6809 100644 --- a/src/components/views/messages/ReactionsRow.js +++ b/src/components/views/messages/ReactionsRow.js @@ -34,6 +34,7 @@ export default class ReactionsRow extends React.PureComponent { if (props.reactions) { props.reactions.on("Relations.add", this.onReactionsChange); + props.reactions.on("Relations.remove", this.onReactionsChange); props.reactions.on("Relations.redaction", this.onReactionsChange); } @@ -45,6 +46,7 @@ export default class ReactionsRow extends React.PureComponent { componentDidUpdate(prevProps) { if (prevProps.reactions !== this.props.reactions) { this.props.reactions.on("Relations.add", this.onReactionsChange); + this.props.reactions.on("Relations.remove", this.onReactionsChange); this.props.reactions.on("Relations.redaction", this.onReactionsChange); this.onReactionsChange(); } @@ -56,6 +58,10 @@ export default class ReactionsRow extends React.PureComponent { "Relations.add", this.onReactionsChange, ); + this.props.reactions.removeListener( + "Relations.remove", + this.onReactionsChange, + ); this.props.reactions.removeListener( "Relations.redaction", this.onReactionsChange, From 006d5d7591279cfe19fd000a4599ca70c91c5570 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 16 May 2019 13:57:02 +0100 Subject: [PATCH 042/273] Fix crash for null reactions set --- src/components/views/messages/ReactionDimension.js | 6 +++++- src/components/views/messages/ReactionsRow.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/ReactionDimension.js b/src/components/views/messages/ReactionDimension.js index 0c91d0ac99..843254ade3 100644 --- a/src/components/views/messages/ReactionDimension.js +++ b/src/components/views/messages/ReactionDimension.js @@ -113,7 +113,11 @@ export default class ReactionDimension extends React.PureComponent { return null; } const userId = MatrixClientPeg.get().getUserId(); - return [...reactions.getAnnotationsBySender()[userId].values()]; + const myReactions = reactions.getAnnotationsBySender()[userId]; + if (!myReactions) { + return null; + } + return [...myReactions.values()]; } onOptionClick = (ev) => { diff --git a/src/components/views/messages/ReactionsRow.js b/src/components/views/messages/ReactionsRow.js index cd3ccb6809..d55ecd6578 100644 --- a/src/components/views/messages/ReactionsRow.js +++ b/src/components/views/messages/ReactionsRow.js @@ -86,7 +86,11 @@ export default class ReactionsRow extends React.PureComponent { return null; } const userId = MatrixClientPeg.get().getUserId(); - return [...reactions.getAnnotationsBySender()[userId].values()]; + const myReactions = reactions.getAnnotationsBySender()[userId]; + if (!myReactions) { + return null; + } + return [...myReactions.values()]; } render() { From f7de8d4f5862f186d9a443f15b966b5a68359e66 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 14:43:32 +0100 Subject: [PATCH 043/273] use new events to find out about replacements --- src/components/structures/TimelinePanel.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 3e79ea7f18..7c1afbe9c3 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -204,11 +204,11 @@ const TimelinePanel = React.createClass({ MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); MatrixClientPeg.get().on("Room.timelineReset", this.onRoomTimelineReset); MatrixClientPeg.get().on("Room.redaction", this.onRoomRedaction); - MatrixClientPeg.get().on("Room.replaceEvent", this.onRoomReplaceEvent); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().on("Room.localEchoUpdated", this.onLocalEchoUpdated); MatrixClientPeg.get().on("Room.accountData", this.onAccountData); MatrixClientPeg.get().on("Event.decrypted", this.onEventDecrypted); + MatrixClientPeg.get().on("Event.replaced", this.onEventReplaced); MatrixClientPeg.get().on("sync", this.onSync); this._initTimeline(this.props); @@ -283,11 +283,11 @@ const TimelinePanel = React.createClass({ client.removeListener("Room.timeline", this.onRoomTimeline); client.removeListener("Room.timelineReset", this.onRoomTimelineReset); client.removeListener("Room.redaction", this.onRoomRedaction); - client.removeListener("Room.replaceEvent", this.onRoomReplaceEvent); client.removeListener("Room.receipt", this.onRoomReceipt); client.removeListener("Room.localEchoUpdated", this.onLocalEchoUpdated); client.removeListener("Room.accountData", this.onAccountData); client.removeListener("Event.decrypted", this.onEventDecrypted); + client.removeListener("Event.replaced", this.onEventReplaced); client.removeListener("sync", this.onSync); } }, @@ -507,7 +507,7 @@ const TimelinePanel = React.createClass({ this.forceUpdate(); }, - onRoomReplaceEvent: function(replacedEvent, room) { + onEventReplaced: function(replacedEvent, room) { if (this.unmounted) return; // ignore events for other rooms From 31817a91c6600ec53093b3e4212eeeae49225cbb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 14:43:47 +0100 Subject: [PATCH 044/273] remove replacements flag --- src/MatrixClientPeg.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 82bd273846..391d089cc6 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -188,8 +188,7 @@ class MatrixClientPeg { timelineSupport: true, forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), verificationMethods: [verificationMethods.SAS], - unstableClientRelationAggregation: aggregateRelations, - unstableClientRelationReplacements: enableEdits, + unstableClientRelationAggregation: aggregateRelations || enableEdits, }; this.matrixClient = createMatrixClient(opts); From 3ef631191be8b5613e2a68b78d2a84afb7e4964b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 14:44:00 +0100 Subject: [PATCH 045/273] show sending status for replacements as well on local echo --- src/components/structures/MessagePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6f21bb6951..dbaab57adf 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -532,7 +532,7 @@ module.exports = React.createClass({ readReceiptMap={this._readReceiptMap} showUrlPreview={this.props.showUrlPreview} checkUnmounting={this._isUnmounting} - eventSendStatus={mxEv.status} + eventSendStatus={mxEv.replacementOrOwnStatus()} tileShape={this.props.tileShape} isTwelveHour={this.props.isTwelveHour} permalinkCreator={this.props.permalinkCreator} From f27607a74cb8b6d2d4f7c724cdae5bfeb6346ff9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 17:58:22 +0100 Subject: [PATCH 046/273] don't put cursor position in NewlinePart after adding it You can't append to it anyway, so mark it uneditable and skip uneditable parts if that's where an edit ended up. This has the added advantage that if there is text after a newly insert pill, the cursor will be put just before it rather than in the pill, after the last character. --- src/editor/model.js | 12 +++++++++++- src/editor/parts.js | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/editor/model.js b/src/editor/model.js index 85dd425b0e..c6f0c5529f 100644 --- a/src/editor/model.js +++ b/src/editor/model.js @@ -88,7 +88,8 @@ export default class EditorModel { } this._mergeAdjacentParts(); const caretOffset = diff.at - removedOffsetDecrease + addedLen; - const newPosition = this._positionForOffset(caretOffset, true); + let newPosition = this._positionForOffset(caretOffset, true); + newPosition = newPosition.skipUneditableParts(this._parts); this._setActivePart(newPosition); this._updateCallback(newPosition); } @@ -261,4 +262,13 @@ class DocumentPosition { get offset() { return this._offset; } + + skipUneditableParts(parts) { + const part = parts[this.index]; + if (part && !part.canEdit) { + return new DocumentPosition(this.index + 1, 0); + } else { + return this; + } + } } diff --git a/src/editor/parts.js b/src/editor/parts.js index a20b857fee..8ba18997a9 100644 --- a/src/editor/parts.js +++ b/src/editor/parts.js @@ -205,6 +205,14 @@ export class NewlinePart extends BasePart { get type() { return "newline"; } + + // this makes the cursor skip this part when it is inserted + // rather than trying to append to it, which is what we want. + // As a newline can also be only one character, it makes sense + // as it can only be one character long. This caused #9741. + get canEdit() { + return false; + } } export class RoomPillPart extends PillPart { From 98e033a5292b34e81fa16cbfe86896a6894041f9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 18:13:48 +0100 Subject: [PATCH 047/273] don't allow newline parts of longer than one newline --- src/editor/parts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/editor/parts.js b/src/editor/parts.js index 8ba18997a9..bf792b1ab9 100644 --- a/src/editor/parts.js +++ b/src/editor/parts.js @@ -57,7 +57,7 @@ class BasePart { appendUntilRejected(str) { for (let i = 0; i < str.length; ++i) { const chr = str.charAt(i); - if (!this.acceptsInsertion(chr)) { + if (!this.acceptsInsertion(chr, i)) { this._text = this._text + str.substr(0, i); return str.substr(i); } @@ -180,8 +180,8 @@ class PillPart extends BasePart { } export class NewlinePart extends BasePart { - acceptsInsertion(chr) { - return this.text.length === 0 && chr === "\n"; + acceptsInsertion(chr, i) { + return (this.text.length + i) === 0 && chr === "\n"; } acceptsRemoval(position, chr) { From 245f48a22c9a7d5de6f2756ad754558f2054835e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 18:39:20 +0100 Subject: [PATCH 048/273] =?UTF-8?q?set=20caret=20on=20mount=20as=20we=20us?= =?UTF-8?q?ually=20do,=20so=20FF=20doesn't=20enter=202=20newlines=20?= =?UTF-8?q?=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/views/elements/MessageEditor.js | 7 +------ src/editor/model.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index b42923954b..b59227ecc8 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -144,12 +144,7 @@ export default class MessageEditor extends React.Component { componentDidMount() { this._updateEditorState(); - const sel = document.getSelection(); - const range = document.createRange(); - range.selectNodeContents(this._editorRef); - range.collapse(false); - sel.removeAllRanges(); - sel.addRange(range); + setCaretPosition(this._editorRef, this.model, this.model.getPositionAtEnd()); this._editorRef.focus(); } diff --git a/src/editor/model.js b/src/editor/model.js index c6f0c5529f..5a571640c6 100644 --- a/src/editor/model.js +++ b/src/editor/model.js @@ -61,6 +61,16 @@ export default class EditorModel { return null; } + getPositionAtEnd() { + if (this._parts.length) { + const index = this._parts.length - 1; + const part = this._parts[index]; + return new DocumentPosition(index, part.text.length); + } else { + return new DocumentPosition(0, 0); + } + } + serializeParts() { return this._parts.map(({type, text}) => {return {type, text};}); } From 690ee63bb460273c10b616b10f301fb97bc468ba Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 16 May 2019 19:14:24 +0100 Subject: [PATCH 049/273] prevent zero-length removals from deleting uneditable parts This solves an issue where, when backspacing the proceeding character next to a pill, chrome reports the caret as being in the pill node, not at the start of the proceeding text node. This would cause the pill to be removed together with proceeding character. This is a bug in any case, removing 0 characters shouldn't remove the part --- src/editor/model.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/editor/model.js b/src/editor/model.js index 5a571640c6..13066897b9 100644 --- a/src/editor/model.js +++ b/src/editor/model.js @@ -183,21 +183,26 @@ export default class EditorModel { // part might be undefined here let part = this._parts[index]; const amount = Math.min(len, part.text.length - offset); - if (part.canEdit) { - const replaceWith = part.remove(offset, amount); - if (typeof replaceWith === "string") { - this._replacePart(index, this._partCreator.createDefaultPart(replaceWith)); - } - part = this._parts[index]; - // remove empty part - if (!part.text.length) { - this._removePart(index); + // don't allow 0 amount deletions + if (amount) { + if (part.canEdit) { + const replaceWith = part.remove(offset, amount); + if (typeof replaceWith === "string") { + this._replacePart(index, this._partCreator.createDefaultPart(replaceWith)); + } + part = this._parts[index]; + // remove empty part + if (!part.text.length) { + this._removePart(index); + } else { + index += 1; + } } else { - index += 1; + removedOffsetDecrease += offset; + this._removePart(index); } } else { - removedOffsetDecrease += offset; - this._removePart(index); + index += 1; } len -= amount; offset = 0; From bc5227a191adf6fbc5d2f5bf7bc5086b9336ac6e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 16 May 2019 14:35:43 -0600 Subject: [PATCH 050/273] Fix some source strings noticed as incorrect by translators --- src/CallHandler.js | 2 +- src/GroupAddressPicker.js | 2 +- src/RoomInvite.js | 4 ++-- src/SlashCommands.js | 2 +- src/components/structures/GroupView.js | 2 +- src/i18n/strings/en_EN.json | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index acdc3e5122..e47209eebe 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -361,7 +361,7 @@ async function _startCallApp(roomId, type) { Modal.createTrackedDialog('Could not connect to the integration server', '', ErrorDialog, { title: _t('Could not connect to the integration server'), - description: _t('A conference call could not be started because the intgrations server is not available'), + description: _t('A conference call could not be started because the integrations server is not available'), }); return; } diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index 532ee23c25..cd5ecc790d 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -38,7 +38,7 @@ export function showGroupInviteDialog(groupId) { Modal.createTrackedDialog('Group Invite', '', AddressPickerDialog, { title: _t("Invite new community members"), description: description, - placeholder: _t("Name or matrix ID"), + placeholder: _t("Name or Matrix ID"), button: _t("Invite to Community"), validAddressTypes: ['mx-user-id'], onFinished: (success, addrs) => { diff --git a/src/RoomInvite.js b/src/RoomInvite.js index b808b935a6..34b9635780 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -45,7 +45,7 @@ export function showStartChatInviteDialog() { Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, { title: _t('Start a chat'), description: _t("Who would you like to communicate with?"), - placeholder: _t("Email, name or matrix ID"), + placeholder: _t("Email, name or Matrix ID"), validAddressTypes: ['mx-user-id', 'email'], button: _t("Start Chat"), onFinished: _onStartChatFinished, @@ -58,7 +58,7 @@ export function showRoomInviteDialog(roomId) { title: _t('Invite new room members'), description: _t('Who would you like to add to this room?'), button: _t('Send Invites'), - placeholder: _t("Email, name or matrix ID"), + placeholder: _t("Email, name or Matrix ID"), onFinished: (shouldInvite, addrs) => { _onRoomInviteFinished(roomId, shouldInvite, addrs); }, diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 55107db899..f25bc9af07 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -518,7 +518,7 @@ export const CommandMap = { unban: new Command({ name: 'unban', args: '', - description: _td('Unbans user with given id'), + description: _td('Unbans user with given ID'), runFn: function(roomId, args) { if (args) { const matches = args.match(/^(\S+)$/); diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index dcbe212267..cdfbe26fea 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -265,7 +265,7 @@ const RoleUserList = React.createClass({ Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, { title: _t('Add users to the community summary'), description: _t("Who would you like to add to this summary?"), - placeholder: _t("Name or matrix ID"), + placeholder: _t("Name or Matrix ID"), button: _t("Add to summary"), validAddressTypes: ['mx-user-id'], groupId: this.props.groupId, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 24e448da09..121845567b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -35,7 +35,7 @@ "You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.", "You cannot place a call with yourself.": "You cannot place a call with yourself.", "Could not connect to the integration server": "Could not connect to the integration server", - "A conference call could not be started because the intgrations server is not available": "A conference call could not be started because the intgrations server is not available", + "A conference call could not be started because the integrations server is not available": "A conference call could not be started because the integrations server is not available", "Call in Progress": "Call in Progress", "A call is currently being placed!": "A call is currently being placed!", "A call is already in progress!": "A call is already in progress!", @@ -80,7 +80,7 @@ "Who would you like to add to this community?": "Who would you like to add to this community?", "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID", "Invite new community members": "Invite new community members", - "Name or matrix ID": "Name or matrix ID", + "Name or Matrix ID": "Name or Matrix ID", "Invite to Community": "Invite to Community", "Which rooms would you like to add to this community?": "Which rooms would you like to add to this community?", "Show these rooms to non-members on the community page and room list?": "Show these rooms to non-members on the community page and room list?", @@ -109,7 +109,7 @@ "Admin": "Admin", "Start a chat": "Start a chat", "Who would you like to communicate with?": "Who would you like to communicate with?", - "Email, name or matrix ID": "Email, name or matrix ID", + "Email, name or Matrix ID": "Email, name or Matrix ID", "Start Chat": "Start Chat", "Invite new room members": "Invite new room members", "Who would you like to add to this room?": "Who would you like to add to this room?", @@ -157,7 +157,7 @@ "Unrecognised room alias:": "Unrecognised room alias:", "Kicks user with given id": "Kicks user with given id", "Bans user with given id": "Bans user with given id", - "Unbans user with given id": "Unbans user with given id", + "Unbans user with given ID": "Unbans user with given ID", "Ignores a user, hiding their messages from you": "Ignores a user, hiding their messages from you", "Ignored user": "Ignored user", "You are now ignoring %(userId)s": "You are now ignoring %(userId)s", From d7c686918866cc0262b3bda251a8768b61315ec8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 16 May 2019 21:55:17 +0100 Subject: [PATCH 051/273] Apply Flex voodoo Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/dialogs/_DevtoolsDialog.scss | 9 +++++++-- src/autocomplete/UserProvider.js | 2 +- src/components/views/dialogs/DevtoolsDialog.js | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 2f01f3ecc6..1f5d36b57a 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -82,8 +82,13 @@ limitations under the License. display: inline-block; } -.mx_DevTools_content .mx_Field_input + .mx_Field_input { - margin-left: 42px; +.mx_DevTools_eventTypeStateKeyGroup { + display: flex; + flex-wrap: wrap; +} + +.mx_DevTools_content .mx_Field_input:first-of-type { + margin-right: 42px; } .mx_DevTools_tgl { diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index d4a5ec5e74..e1dd8ea620 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -119,7 +119,7 @@ export default class UserProvider extends AutocompleteProvider { component: ( } - title={displayName} + title={displayName}mx_DevTools_content description={user.userId} /> ), range, diff --git a/src/components/views/dialogs/DevtoolsDialog.js b/src/components/views/dialogs/DevtoolsDialog.js index 0835c41bb9..9327e1e54e 100644 --- a/src/components/views/dialogs/DevtoolsDialog.js +++ b/src/components/views/dialogs/DevtoolsDialog.js @@ -128,8 +128,10 @@ class SendCustomEvent extends GenericEditor { return
- { this.textInput('eventType', _t('Event Type')) } - { this.state.isStateEvent && this.textInput('stateKey', _t('State Key')) } +
+ { this.textInput('eventType', _t('Event Type')) } + { this.state.isStateEvent && this.textInput('stateKey', _t('State Key')) } +

From 82bd893f032052d09a142ffd865c966428dc9c15 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 16 May 2019 16:36:23 -0600 Subject: [PATCH 052/273] Mute screen readers over reactions --- src/components/views/messages/ReactionDimension.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/messages/ReactionDimension.js b/src/components/views/messages/ReactionDimension.js index 843254ade3..de33ad1a57 100644 --- a/src/components/views/messages/ReactionDimension.js +++ b/src/components/views/messages/ReactionDimension.js @@ -168,6 +168,7 @@ export default class ReactionDimension extends React.PureComponent { return {items} ; From b2aad4afb10eb6d65ba6ee1023dff29f3951e0d8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 17 May 2019 00:28:45 +0100 Subject: [PATCH 053/273] remove accidental paste Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/autocomplete/UserProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index e1dd8ea620..d4a5ec5e74 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -119,7 +119,7 @@ export default class UserProvider extends AutocompleteProvider { component: ( } - title={displayName}mx_DevTools_content + title={displayName} description={user.userId} /> ), range, From ca2e6d8eb2475f4cb4159c501a3afaaefd75b55e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 10:22:02 +0100 Subject: [PATCH 054/273] Message editing: shift+enter for newline, enter to send --- .../views/elements/MessageEditor.js | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index b59227ecc8..c18d1f56fd 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -77,35 +77,44 @@ export default class MessageEditor extends React.Component { } _onKeyDown = (event) => { + // insert newline on Shift+Enter + if (event.shiftKey && event.key === "Enter") { + event.preventDefault(); // just in case the browser does support this + document.execCommand("insertHTML", undefined, "\n"); + return; + } + // autocomplete or enter to send below shouldn't have any modifier keys pressed. if (event.metaKey || event.altKey || event.shiftKey) { return; } - if (!this.model.autoComplete) { - return; + if (this.model.autoComplete) { + const autoComplete = this.model.autoComplete; + switch (event.key) { + case "Enter": + autoComplete.onEnter(event); break; + case "ArrowUp": + autoComplete.onUpArrow(event); break; + case "ArrowDown": + autoComplete.onDownArrow(event); break; + case "Tab": + autoComplete.onTab(event); break; + case "Escape": + autoComplete.onEscape(event); break; + default: + return; // don't preventDefault on anything else + } + event.preventDefault(); + } else if (event.key === "Enter") { + this._sendEdit(); + event.preventDefault(); } - const autoComplete = this.model.autoComplete; - switch (event.key) { - case "Enter": - autoComplete.onEnter(event); break; - case "ArrowUp": - autoComplete.onUpArrow(event); break; - case "ArrowDown": - autoComplete.onDownArrow(event); break; - case "Tab": - autoComplete.onTab(event); break; - case "Escape": - autoComplete.onEscape(event); break; - default: - return; // don't preventDefault on anything else - } - event.preventDefault(); } _onCancelClicked = () => { dis.dispatch({action: "edit_event", event: null}); } - _onSaveClicked = () => { + _sendEdit = () => { const newContent = { "msgtype": "m.text", "body": textSerialize(this.model), @@ -177,7 +186,7 @@ export default class MessageEditor extends React.Component { >
{_t("Cancel")} - {_t("Save")} + {_t("Save")}
; } From c0cfa8ad00cd7f92d2a61db0de8839f4accbe56d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 10:26:19 +0100 Subject: [PATCH 055/273] only allow editing of text messages --- src/utils/EventUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.js index ac415ca6de..81aa82057b 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -47,5 +47,6 @@ export function isContentActionable(mxEvent) { export function canEditContent(mxEvent) { return isContentActionable(mxEvent) && + mxEvent.getContent().msgtype === "m.text" && mxEvent.getSender() === MatrixClientPeg.get().getUserId(); } From e731ce32b8c642dd59b642618c058e1453c5452e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 16 May 2019 20:31:29 +0000 Subject: [PATCH 056/273] Translated using Weblate (English (United States)) Currently translated at 44.1% (718 of 1628 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/en_US/ --- src/i18n/strings/en_US.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 1c6f782db4..8829b1a421 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -887,5 +887,8 @@ "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s changed their display name to %(displayName)s.", "Spanner": "Wrench", "Aeroplane": "Airplane", - "Cat": "Cat" + "Cat": "Cat", + "Sends the given message coloured as a rainbow": "Sends the given message colored as a rainbow", + "Sends the given emote coloured as a rainbow": "Sends the given emote colored as a rainbow", + "Unrecognised address": "Unrecognized address" } From 2beb854d92afd4a4b0828788ed4d10333770d46f Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Thu, 16 May 2019 09:53:33 +0000 Subject: [PATCH 057/273] Translated using Weblate (Finnish) Currently translated at 98.9% (1610 of 1628 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 89 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 76448ddcb2..515ff416c1 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -426,7 +426,7 @@ "Start new chat": "Aloita uusi keskustelu", "Failed to invite": "Kutsu epäonnistui", "Failed to invite user": "Käyttäjän kutsuminen epäonnistui", - "Failed to invite the following users to the %(roomName)s room:": "Seuraavian käyttäjien kutsuminen huoneeseen %(roomName)s epäonnistui:", + "Failed to invite the following users to the %(roomName)s room:": "Seuraavien käyttäjien kutsuminen huoneeseen %(roomName)s epäonnistui:", "Confirm Removal": "Varmista poistaminen", "Unknown error": "Tuntematon virhe", "Incorrect password": "Virheellinen salasana", @@ -469,7 +469,7 @@ "Riot does not have permission to send you notifications - please check your browser settings": "Riotilla ei ole oikeuksia lähettää sinulle ilmoituksia. Ole hyvä ja tarkista selaimen asetukset", "Riot was not given permission to send notifications - please try again": "Riot ei saannut lupaa lähettää ilmoituksia. Ole hyvä ja yritä uudelleen", "Room %(roomId)s not visible": "Huone %(roomId)s ei ole näkyvissä", - "%(roomName)s does not exist.": "%(roomName)s ei ole olemassa.", + "%(roomName)s does not exist.": "Huonetta %(roomName)s ei ole olemassa.", "%(roomName)s is not accessible at this time.": "%(roomName)s ei ole saatavilla tällä hetkellä.", "Seen by %(userName)s at %(dateTime)s": "Käyttäjän %(userName)s näkemä %(dateTime)s", "Send Reset Email": "Lähetä salasanan palautusviesti", @@ -943,7 +943,7 @@ "Send Custom Event": "Lähetä mukautettu tapahtuma", "Advanced notification settings": "Lisäasetukset ilmoituksille", "delete the alias.": "poista alias.", - "To return to your account in future you need to set a password": "Voidaksesi tulevaisuudessa palata tilillesi sinun pitää asettaa salasana", + "To return to your account in future you need to set a password": "Jotta voit jatkossa palata tilillesi, sinun pitää asettaa salasana", "Forget": "Unohda", "#example": "#esimerkki", "Hide panel": "Piilota paneeli", @@ -1346,11 +1346,11 @@ "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Jos olet aikaisemmin käyttänyt uudempaa versiota Riotista, istuntosi voi olla epäyhteensopiva tämän version kanssa. Sulje tämä ikkuna ja yritä uudemman version kanssa.", "The platform you're on": "Alusta, jolla olet", "Whether or not you're logged in (we don't record your username)": "Riippumatta siitä oletko kirjautunut sisään (emme tallenna käyttäjätunnustasi)", - "Whether or not you're using the Richtext mode of the Rich Text Editor": "Riippumatta siitä, että käytätkö muotoillun tekstin tilaa muotoilueditorissa", + "Whether or not you're using the Richtext mode of the Rich Text Editor": "Riippumatta siitä, käytätkö muotoillun tekstin tilaa muotoilueditorissa", "Your User Agent": "Selaintunnisteesi", "The information being sent to us to help make Riot.im better includes:": "Tietoihin, jota lähetetään Riot.im:ään palvelun parantamiseksi, sisältyy:", "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Niissä kohdissa, missä tämä sivu sisältää yksilöivää tietoa, kuten huoneen, käyttäjän tai ryhmän ID:n, kyseinen tieto poistetaan ennen tiedon lähetystä palvelimelle.", - "A conference call could not be started because the intgrations server is not available": "Konferenssipuhelua ei pystytty aloittamaan, koska integraatiopalvelin ei ole käytettävissä", + "A conference call could not be started because the intgrations server is not available": "Konferenssipuhelua ei voitu aloittaa, koska integraatiopalvelin ei ole käytettävissä", "A call is currently being placed!": "Puhelua ollaan aloittamassa!", "A call is already in progress!": "Puhelu on jo meneillään!", "Permission Required": "Lisäoikeuksia tarvitaan", @@ -1760,5 +1760,82 @@ "Recovery Method Removed": "Palautustapa poistettu", "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Tämä laite on huomannut, että palautuksen salalauseesi ja avaimesi salatuille viesteille on poistettu.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Jos teit tämän vahingossa, voit ottaa käyttöön salatut viestit tälle laitteelle, joka uudelleensalaa tämän laitteen keskusteluhistorian uudella palautustavalla.", - "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Jos et poistanut palautustapaa, hyökkääjä saattaa yrittää käyttää tiliäsi. Vaihda tilisi salasana ja aseta uusi palautustapa asetuksissa välittömästi." + "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Jos et poistanut palautustapaa, hyökkääjä saattaa yrittää käyttää tiliäsi. Vaihda tilisi salasana ja aseta uusi palautustapa asetuksissa välittömästi.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Riippumatta siitä, käytätkö 'leivänmuruja' (kuvia huonelistan yläpuolella)", + "Replying With Files": "Tiedostoilla vastaaminen", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Tiedostolla vastaaminen ei onnistu tällä kertaa. Haluatko ladata tiedoston vastaamatta?", + "The file '%(fileName)s' failed to upload.": "Tiedoston '%(fileName)s' lataaminen ei onnistunut.", + "The server does not support the room version specified.": "Palvelin ei tue määritettyä huoneversiota.", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Vahvista, että haluat päivittää huoneen versiosta versioon .", + "Changes your avatar in this current room only": "Vaihtaa kuvasi vain nykyisessä huoneessa", + "Sends the given message coloured as a rainbow": "Lähettää viestin sateenkaaren väreissä", + "Sends the given emote coloured as a rainbow": "Lähettää emoten sateenkaaren väreissä", + "The user's homeserver does not support the version of the room.": "Käyttäjän kotipalvelin ei tue huoneen versiota.", + "Show recent room avatars above the room list": "Näytä viimeaikaiset huoneen kuvat huoneluettelon yläpuolella", + "Edit messages after they have been sent (refresh to apply changes)": "Muokkaa viestejä niiden lähettämisen jälkeen (päivitä saattaaksesi muutokset voimaan)", + "React to messages with emoji (refresh to apply changes)": "Reagoi viesteihin emojeilla (päivitä saattaaksesi muutokset voimaan)", + "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Tämä laite ei varmuuskopioi avaimiasi, mutta sinulla on olemassa varmuuskopio palauttamista ja lisäämistä varten.", + "Backup has an invalid signature from this device": "Varmuuskopiossa on epäkelpo allekirjoitus tältä laitteelta", + "this room": "tämä huone", + "View older messages in %(roomName)s.": "Näytä vanhemmat viestit huoneessa %(roomName)s.", + "Joining room …": "Liitytään huoneeseen …", + "Loading …": "Latataan …", + "Join the conversation with an account": "Liity keskusteluun tilin avulla", + "Sign Up": "Rekisteröidy", + "Sign In": "Kirjaudu", + "Reason: %(reason)s": "Syy: %(reason)s", + "Forget this room": "Unohda tämä huone", + "Re-join": "Liity uudelleen", + "You were banned from %(roomName)s by %(memberName)s": "%(memberName)s antoi sinulle porttikiellon huoneeseen %(roomName)s", + "Something went wrong with your invite to %(roomName)s": "Jotain meni vikaan kutsussasi huoneeseen %(roomName)s", + "%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.": "Kutsusi validointi palautti virhekoodin %(errcode)s. Voit koettaa välittää tiedon huoneen ylläpitäjälle.", + "You can only join it with a working invite.": "Voit liittyä siihen vain toimivalla kutsulla.", + "You can still join it because this is a public room.": "Voit silti liittyä siihen, koska huone on julkinen.", + "Join the discussion": "Liity keskusteluun", + "Try to join anyway": "Yritä silti liittyä", + "This invite to %(roomName)s wasn't sent to your account": "Kutsua huoneeseen %(roomName)s ei lähetetty tilillesi", + "Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.": "Kirjaudu eri tilillä, pyydä uutta kutsua tai lisää sähköpostiosoite %(email)s tähän tiliin.", + "Do you want to chat with %(user)s?": "Haluatko keskustella käyttäjän %(user)s kanssa?", + "Do you want to join %(roomName)s?": "Haluatko liittyä huoneeseen %(roomName)s?", + " invited you": " kutsui sinut", + "You're previewing %(roomName)s. Want to join it?": "Esikatselet huonetta %(roomName)s. Haluatko liittyä siihen?", + "%(roomName)s can't be previewed. Do you want to join it?": "Huonetta %(roomName)s ei voi esikatsella. Haluatko liittyä siihen?", + "This room doesn't exist. Are you sure you're at the right place?": "Tätä huonetta ei ole olemassa. Oletko varma, että olet oikeassa paikassa?", + "This room has already been upgraded.": "Tämä huone on jo päivitetty.", + "Rotate Left": "Kierrä vasempaan", + "Rotate counter-clockwise": "Kierrä vastapäivään", + "Rotate Right": "Kierrä oikeaan", + "Rotate clockwise": "Kierrä myötäpäivään", + "View Servers in Room": "Näytä huoneessa olevat palvelimet", + "Sign out and remove encryption keys?": "Kirjaudu ulos ja poista salausavaimet?", + "Missing session data": "Istunnon dataa puuttuu", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Istunnon dataa, mukaanlukien salausavaimia, puuttuu. Kirjaudu ulos ja sisään, jolloin avaimet palautetaan varmuuskopiosta.", + "Your browser likely removed this data when running low on disk space.": "Selaimesi luultavasti poisti tämän datan, kun levytila oli vähissä.", + "Upload files (%(current)s of %(total)s)": "Lataa tiedostot (%(current)s / %(total)s)", + "Upload files": "Lataa tiedostot", + "These files are too large to upload. The file size limit is %(limit)s.": "Tiedostot ovat liian isoja ladattaviksi. Tiedoston kokoraja on %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Osa tiedostoista on liian isoja ladattaviksi. Tiedoston kokoraja on %(limit)s.", + "Upload %(count)s other files|other": "Lataa %(count)s muuta tiedostoa", + "Upload %(count)s other files|one": "Lataa %(count)s muu tiedosto", + "Cancel All": "Peruuta kaikki", + "Upload Error": "Latausvirhe", + "Use an email address to recover your account": "Palauta tilisi sähköpostiosoitteen avulla", + "Enter email address (required on this homeserver)": "Syötä sähköpostiosoite (vaaditaan tällä kotipalvelimella)", + "Doesn't look like a valid email address": "Ei näytä kelvolliselta sähköpostiosoitteelta", + "Enter password": "Syötä salasana", + "Password is allowed, but unsafe": "Salasana on sallittu, mutta turvaton", + "Nice, strong password!": "Hyvä, vahva salasana!", + "Passwords don't match": "Salasanat eivät täsmää", + "Other users can invite you to rooms using your contact details": "Muut voivat kutsua sinut huoneisiin yhteystietojesi avulla", + "Enter phone number (required on this homeserver)": "Syötä puhelinnumero (vaaditaan tällä kotipalvelimella)", + "Doesn't look like a valid phone number": "Ei näytä kelvolliselta puhelinnumerolta", + "Use letters, numbers, dashes and underscores only": "Käytä vain kirjaimia, numeroita, viivoja ja alaviivoja", + "Enter username": "Syötä käyttäjänimi", + "Some characters not allowed": "Osaa merkeistä ei sallita", + "Use an email address to recover your account.": "Palauta tilisi sähköpostiosoitteen avulla.", + "Other users can invite you to rooms using your contact details.": "Muut käyttäjät voivat kutsua sinut huoneisiin yhteystietojesi avulla.", + "Error loading Riot": "Virhe Riotin lataamisessa", + "If this is unexpected, please contact your system administrator or technical support representative.": "Jos et odottanut tätä, ota yhteyttä järjestelmänvalvojaan tai tekniseen tukeen.", + "Homeserver URL does not appear to be a valid Matrix homeserver": "Kotipalvelimen osoite ei näytä olevan kelvollinen Matrix-kotipalvelin", + "Identity server URL does not appear to be a valid identity server": "Identiteettipalvelimen osoite ei näytä olevan kelvollinen identiteettipalvelin" } From 34bd70f738d3a9f8d94bce06cfa6b358b1709835 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 16 May 2019 15:58:15 +0000 Subject: [PATCH 058/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1628 of 1628 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 72 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index d0a4560dc0..c758badebb 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1892,5 +1892,75 @@ "Upload %(count)s other files|other": "Feltölt %(count)s másik fájlt", "Upload %(count)s other files|one": "Feltölt %(count)s másik fájlt", "Cancel All": "Mindent megszakít", - "Upload Error": "Feltöltési hiba" + "Upload Error": "Feltöltési hiba", + "The server does not support the room version specified.": "A szerver nem támogatja a megadott szoba verziót.", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Kérlek erősítsd meg, hogy a szobát frissíted a verzióról verzióra.", + "Changes your avatar in this current room only": "A profilképedet csak ebben a szobában változtatja meg", + "Sends the given message coloured as a rainbow": "A megadott üzenetet szivárvány színben küldi el", + "Sends the given emote coloured as a rainbow": "A megadott hangulatjelet szivárvány színben küldi el", + "The user's homeserver does not support the version of the room.": "A felhasználó matrix szervere nem támogatja a megadott szoba verziót.", + "Edit messages after they have been sent (refresh to apply changes)": "Üzenet szerkesztése küldés után (újratöltés szükséges)", + "React to messages with emoji (refresh to apply changes)": "Reagálj az üzenetre emoji-val (újratöltés szükséges)", + "When rooms are upgraded": "Ha a szobák frissültek", + "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Ez az eszköz nem menti el a kulcsaidat, de létezik mentés amit visszaállíthatsz és folytathatod.", + "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Csatlakozz ezzel az eszközzel a kulcs mentéshez kilépés előtt, hogy ne veszíts el kulcsot ami esetleg csak ezen az eszközön van meg.", + "Connect this device to Key Backup": "Csatlakozz ezzel az eszközzel a Kulcs Mentéshez", + "Backup has an invalid signature from this device": "A mentés érvénytelen aláírással rendelkezik erről az eszközről", + "this room": "ez a szoba", + "View older messages in %(roomName)s.": "Régebbi üzenetek megjelenítése itt: %(roomName)s.", + "Joining room …": "Szobához csatlakozás …", + "Loading …": "Betöltés …", + "Rejecting invite …": "Meghívó elutasítása …", + "Join the conversation with an account": "Beszélgetéshez csatlakozás felhasználói fiókkal", + "Sign Up": "Fiók készítés", + "Sign In": "Bejelentkezés", + "You were kicked from %(roomName)s by %(memberName)s": "Téged kirúgott %(memberName)s ebből a szobából: %(roomName)s", + "Reason: %(reason)s": "Ok: %(reason)s", + "Forget this room": "Szoba elfelejtése", + "Re-join": "Újra-csatlakozás", + "You were banned from %(roomName)s by %(memberName)s": "Téged kitiltott %(memberName)s ebből a szobából: %(roomName)s", + "Something went wrong with your invite to %(roomName)s": "A meghívóddal ebbe a szobába: %(roomName)s valami baj történt", + "%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.": "A meghívód ellenőrzése során az alábbi hibakódot kaptuk: %(errcode)s. Megpróbálhatod ezt az információt átadni a szoba adminisztrátorának.", + "You can only join it with a working invite.": "Csak érvényes meghívóval tudsz csatlakozni.", + "You can still join it because this is a public room.": "Mivel a szoba nyilvános megpróbálhatsz csatlakozni.", + "Join the discussion": "Beszélgetéshez csatlakozás", + "Try to join anyway": "Mindennek ellenére próbálj csatlakozni", + "This invite to %(roomName)s wasn't sent to your account": "Ezt a meghívót ide: %(roomName)s nem a te fiókodnak küldték", + "Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.": "Jelentkezz be más fiókkal, kérj másik meghívót vagy add hozzá a fiókodhoz ezt az e-mail címet: %(email)s.", + "Do you want to chat with %(user)s?": "%(user)s felhasználóval szeretnél beszélgetni?", + "Do you want to join %(roomName)s?": "%(roomName)s szobába szeretnél belépni?", + " invited you": " meghívott", + "You're previewing %(roomName)s. Want to join it?": "%(roomName)s szoba előnézetét látod. Belépsz?", + "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s szobának nincs előnézete. Be szeretnél lépni?", + "This room doesn't exist. Are you sure you're at the right place?": "Ez a szoba nem létezik. Biztos, hogy jó helyen vagy?", + "Try again later, or ask a room admin to check if you have access.": "Próbálkozz később vagy kérd meg a szoba adminisztrátorát, hogy nézze meg van-e hozzáférésed.", + "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "Amikor a szobát próbáltuk elérni ezt a hibaüzenetet kaptuk: %(errcode)s. Ha úgy gondolod, hogy ez egy hiba légy szívesnyiss egy hibajegyet.", + "This room has already been upgraded.": "Ez a szoba már frissült.", + "Agree or Disagree": "Egyetért vagy Ellentmond", + "Like or Dislike": "Kedveli vagy Nem kedveli", + "Rotate Left": "Balra forgat", + "Rotate Right": "Jobbra forgat", + "View Servers in Room": "Szerverek megjelenítése a szobában", + "Use an email address to recover your account": "A felhasználói fiók visszaszerzése e-mail címmel", + "Enter email address (required on this homeserver)": "E-mail cím megadása (ezen a matrix szerveren kötelező)", + "Doesn't look like a valid email address": "Az e-mail cím nem tűnik érvényesnek", + "Enter password": "Jelszó megadása", + "Password is allowed, but unsafe": "A jelszó engedélyezett, de nem biztonságos", + "Nice, strong password!": "Szép, erős jelszó!", + "Passwords don't match": "A jelszavak nem egyeznek meg", + "Other users can invite you to rooms using your contact details": "Mások meghívhatnak a szobákba a kapcsolatoknál megadott adataiddal", + "Enter phone number (required on this homeserver)": "Telefonszám megadása (ennél a matrix szervernél kötelező)", + "Doesn't look like a valid phone number": "Ez a telefonszám nem tűnik érvényesnek", + "Use letters, numbers, dashes and underscores only": "Csak betűket, számokat, kötőjelet és aláhúzást használj", + "Enter username": "Felhasználói név megadása", + "Some characters not allowed": "Néhány karakter nem engedélyezett", + "Use an email address to recover your account.": "A felhasználói fiókod visszaszerzéséhez használd az e-mail címet.", + "Other users can invite you to rooms using your contact details.": "Mások meghívhatnak a szobákba a kapcsolatoknál megadott adataid alapján.", + "Error loading Riot": "A Riot betöltésénél hiba", + "If this is unexpected, please contact your system administrator or technical support representative.": "Ha ez váratlanul ért, kérlek vedd fel a kapcsolatot a rendszer adminisztrátorával vagy a technikai segítséggel.", + "Failed to get autodiscovery configuration from server": "A szerverről nem sikerült beszerezni az automatikus felderítés beállításait", + "Invalid base_url for m.homeserver": "Hibás base_url az m.homeserver -hez", + "Homeserver URL does not appear to be a valid Matrix homeserver": "A matrix URL nem tűnik érvényesnek", + "Invalid base_url for m.identity_server": "Érvénytelen base_url az m.identity_server -hez", + "Identity server URL does not appear to be a valid identity server": "Az Azonosító szerver URL nem tűnik érvényesnek" } From 317898048ec4b1e14292c1d61046697b5a1a6359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0?= Date: Thu, 16 May 2019 15:45:52 +0000 Subject: [PATCH 059/273] Translated using Weblate (Slovenian) Currently translated at 0.7% (11 of 1628 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sl/ --- src/i18n/strings/sl.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sl.json b/src/i18n/strings/sl.json index 0967ef424b..39297c8ae9 100644 --- a/src/i18n/strings/sl.json +++ b/src/i18n/strings/sl.json @@ -1 +1,13 @@ -{} +{ + "This email address is already in use": "Ta e-poštni naslov je že v uporabi", + "This phone number is already in use": "Ta telefonska številka je že v uporabi", + "Failed to verify email address: make sure you clicked the link in the email": "E-poštnega naslova ni bilo mogoče preveriti: preverite, ali ste kliknili povezavo v e-poštnem sporočilu", + "The platform you're on": "Vaša platforma", + "The version of Riot.im": "Različica Riot.im", + "Dismiss": "Opusti", + "Chat with Riot Bot": "Klepetajte z Riot Botom", + "Sign In": "Prijava", + "powered by Matrix": "poganja Matrix", + "Custom Server Options": "Možnosti strežnika po meri", + "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.": "Nastavite lahko tudi strežnik za identiteto po meri, vendar ne boste mogli povabiti uporabnikov prek e-pošte, prav tako pa vas ne bodo mogli povabiti drugi." +} From 4a6725d4c2e12c6694d8f6bd186b1bdc132057b7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 11:36:36 +0100 Subject: [PATCH 060/273] Message editing: show (edited) marker on edited messages, with tooltip --- res/css/views/rooms/_EventTile.scss | 8 ++++++ src/components/views/messages/TextualBody.js | 30 ++++++++++++++++++++ src/i18n/strings/en_EN.json | 2 ++ 3 files changed, 40 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index f4c12bb734..8f67069c82 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -377,6 +377,14 @@ limitations under the License. left: 41px; } +.mx_EventTile_content .mx_EventTile_edited { + user-select: none; + font-size: 12px; + color: $roomtopic-color; + display: inline-block; + margin-left: 9px; +} + /* Various markdown overrides */ .mx_EventTile_content .markdown-body { diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 34769c060f..0ca4711b07 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -22,6 +22,7 @@ import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import highlight from 'highlight.js'; import * as HtmlUtils from '../../../HtmlUtils'; +import {formatDate} from '../../../DateUtils'; import sdk from '../../../index'; import ScalarAuthClient from '../../../ScalarAuthClient'; import Modal from '../../../Modal'; @@ -148,6 +149,7 @@ module.exports = React.createClass({ nextProps.highlightLink !== this.props.highlightLink || nextProps.showUrlPreview !== this.props.showUrlPreview || nextState.links !== this.state.links || + nextState.editedMarkerHovered !== this.state.editedMarkerHovered || nextState.widgetHidden !== this.state.widgetHidden); }, @@ -432,6 +434,31 @@ module.exports = React.createClass({ }); }, + _onMouseEnterEditedMarker: function() { + this.setState({editedMarkerHovered: true}); + }, + + _onMouseLeaveEditedMarker: function() { + this.setState({editedMarkerHovered: false}); + }, + + _renderEditedMarker: function() { + let editedTooltip; + if (this.state.editedMarkerHovered) { + const Tooltip = sdk.getComponent('elements.Tooltip'); + const editEvent = this.props.mxEvent.replacingEvent(); + const date = editEvent && formatDate(editEvent.getDate()); + editedTooltip = ; + } + return ( +
{editedTooltip}{`(${_t("Edited")})`}
+ ); + }, + render: function() { const EmojiText = sdk.getComponent('elements.EmojiText'); const mxEvent = this.props.mxEvent; @@ -443,6 +470,9 @@ module.exports = React.createClass({ // Part of Replies fallback support stripReplyFallback: stripReply, }); + if (this.props.replacingEventId) { + body = [body, this._renderEditedMarker()]; + } if (this.props.highlightLink) { body = { body }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 121845567b..f535549232 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -916,6 +916,8 @@ "Failed to copy": "Failed to copy", "Add an Integration": "Add an Integration", "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", + "Edited at %(date)s.": "Edited at %(date)s.", + "Edited": "Edited", "Removed or unknown message type": "Removed or unknown message type", "Message removed by %(userId)s": "Message removed by %(userId)s", "Message removed": "Message removed", From 32c68feae2c5c66af26415e9dd7611b29e12fad5 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 11:53:44 +0100 Subject: [PATCH 061/273] Run translation substitution in 2 passes By first substituting variables and then tags after, the translation handling can now support strings with variables inside tags, such as: "people reacted with %(foo)s" --- src/languageHandler.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index 854ac079bc..bdef829933 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -125,20 +125,25 @@ export function _t(text, variables, tags) { * @return a React component if any non-strings were used in substitutions, otherwise a string */ export function substitute(text, variables, tags) { - const regexpMapping = {}; + let result = text; if (variables !== undefined) { + const regexpMapping = {}; for (const variable in variables) { regexpMapping[`%\\(${variable}\\)s`] = variables[variable]; } + result = replaceByRegexes(result, regexpMapping); } if (tags !== undefined) { + const regexpMapping = {}; for (const tag in tags) { regexpMapping[`(<${tag}>(.*?)<\\/${tag}>|<${tag}>|<${tag}\\s*\\/>)`] = tags[tag]; } + result = replaceByRegexes(result, regexpMapping); } - return replaceByRegexes(text, regexpMapping); + + return result; } /* From 3da1f73ea40b6b76cdbc449778a793b3272f87f6 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 11:52:03 +0100 Subject: [PATCH 062/273] Add a basic tooltip showing who reacted This adds a first attempt at tooltip showing who reacted to a message. It doesn't limit senders or position the tooltip nicely, but the info is there at least. Part of https://github.com/vector-im/riot-web/issues/9722 --- res/css/_components.scss | 1 + .../messages/_ReactionsRowButtonTooltip.scss | 34 ++++++++ res/themes/dark/css/_dark.scss | 2 + res/themes/light/css/_light.scss | 2 + src/HtmlUtils.js | 11 +++ src/components/views/messages/ReactionsRow.js | 2 +- .../views/messages/ReactionsRowButton.js | 50 +++++++++++- .../messages/ReactionsRowButtonTooltip.js | 80 +++++++++++++++++++ src/i18n/strings/en_EN.json | 1 + 9 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 res/css/views/messages/_ReactionsRowButtonTooltip.scss create mode 100644 src/components/views/messages/ReactionsRowButtonTooltip.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 2e0c91bd8c..9823b4ac3d 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -119,6 +119,7 @@ @import "./views/messages/_ReactionDimension.scss"; @import "./views/messages/_ReactionsRow.scss"; @import "./views/messages/_ReactionsRowButton.scss"; +@import "./views/messages/_ReactionsRowButtonTooltip.scss"; @import "./views/messages/_RoomAvatarEvent.scss"; @import "./views/messages/_SenderProfile.scss"; @import "./views/messages/_TextualEvent.scss"; diff --git a/res/css/views/messages/_ReactionsRowButtonTooltip.scss b/res/css/views/messages/_ReactionsRowButtonTooltip.scss new file mode 100644 index 0000000000..086271e556 --- /dev/null +++ b/res/css/views/messages/_ReactionsRowButtonTooltip.scss @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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_ReactionsRowButtonTooltip { + box-shadow: none; + background-color: $reaction-row-button-tooltip-bg-color; + color: $reaction-row-button-tooltip-fg-color; + text-align: center; + font-size: 8px; + padding: 6px; + border: none; + border-radius: 3px; + + .mx_Tooltip_chevron::after { + border-right-color: $reaction-row-button-tooltip-bg-color; + } + + .mx_ReactionsRowButtonTooltip_reactedWith { + opacity: 0.7; + } +} diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 592b1a1887..251f038f75 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -156,6 +156,8 @@ $reaction-row-button-border-color: #616b7f; $reaction-row-button-hover-border-color: $header-panel-text-primary-color; $reaction-row-button-selected-bg-color: #1f6954; $reaction-row-button-selected-border-color: $accent-color; +$reaction-row-button-tooltip-bg-color: $tagpanel-bg-color; +$reaction-row-button-tooltip-fg-color: #ffffff; // ***** Mixins! ***** diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index fc15170b87..375b4a44b3 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -264,6 +264,8 @@ $reaction-row-button-border-color: #e9edf1; $reaction-row-button-hover-border-color: $focus-bg-color; $reaction-row-button-selected-bg-color: #e9fff9; $reaction-row-button-selected-border-color: $accent-color; +$reaction-row-button-tooltip-bg-color: $tagpanel-bg-color; +$reaction-row-button-tooltip-fg-color: #ffffff; // ***** Mixins! ***** diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index d9d8bac93b..1032c52e32 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -107,6 +107,17 @@ function unicodeToImage(str, addAlt) { return str; } +/** + * Returns the shortcode for an emoji character. + * + * @param {String} char The emoji character + * @return {String} The shortcode (such as :thumbup:) + */ +export function unicodeToShort(char) { + const unicode = emojione.jsEscapeMap[char]; + return emojione.mapUnicodeToShort()[unicode]; +} + /** * Given one or more unicode characters (represented by unicode * character number), return an image node with the corresponding diff --git a/src/components/views/messages/ReactionsRow.js b/src/components/views/messages/ReactionsRow.js index d55ecd6578..d3bf6a2035 100644 --- a/src/components/views/messages/ReactionsRow.js +++ b/src/components/views/messages/ReactionsRow.js @@ -116,8 +116,8 @@ export default class ReactionsRow extends React.PureComponent { return ; }); diff --git a/src/components/views/messages/ReactionsRowButton.js b/src/components/views/messages/ReactionsRowButton.js index 721147cdb8..19cae27b87 100644 --- a/src/components/views/messages/ReactionsRowButton.js +++ b/src/components/views/messages/ReactionsRowButton.js @@ -19,17 +19,28 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import MatrixClientPeg from '../../../MatrixClientPeg'; +import sdk from '../../../index'; export default class ReactionsRowButton extends React.PureComponent { static propTypes = { // The event we're displaying reactions for mxEvent: PropTypes.object.isRequired, + // The reaction content / key / emoji content: PropTypes.string.isRequired, - count: PropTypes.number.isRequired, + // A Set of Martix reaction events for this key + reactionEvents: PropTypes.object.isRequired, // A possible Matrix event if the current user has voted for this type myReactionEvent: PropTypes.object, } + constructor(props) { + super(props); + + this.state = { + tooltipVisible: false, + }; + } + onClick = (ev) => { const { mxEvent, myReactionEvent, content } = this.props; if (myReactionEvent) { @@ -48,18 +59,53 @@ export default class ReactionsRowButton extends React.PureComponent { } }; + onMouseOver = () => { + this.setState({ + // To avoid littering the DOM with a tooltip for every reaction, + // only render it on first use. + tooltipRendered: true, + tooltipVisible: true, + }); + } + + onMouseOut = () => { + this.setState({ + tooltipVisible: false, + }); + } + render() { - const { content, count, myReactionEvent } = this.props; + const ReactionsRowButtonTooltip = + sdk.getComponent('messages.ReactionsRowButtonTooltip'); + const { content, reactionEvents, myReactionEvent } = this.props; + + const count = reactionEvents.size; + if (!count) { + return null; + } const classes = classNames({ mx_ReactionsRowButton: true, mx_ReactionsRowButton_selected: !!myReactionEvent, }); + let tooltip; + if (this.state.tooltipRendered) { + tooltip = ; + } + return {content} {count} + {tooltip} ; } } diff --git a/src/components/views/messages/ReactionsRowButtonTooltip.js b/src/components/views/messages/ReactionsRowButtonTooltip.js new file mode 100644 index 0000000000..fc4aed0410 --- /dev/null +++ b/src/components/views/messages/ReactionsRowButtonTooltip.js @@ -0,0 +1,80 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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 MatrixClientPeg from '../../../MatrixClientPeg'; +import sdk from '../../../index'; +import { unicodeToShort } from '../../../HtmlUtils'; +import { _t } from '../../../languageHandler'; + +export default class ReactionsRowButtonTooltip extends React.PureComponent { + static propTypes = { + // The event we're displaying reactions for + mxEvent: PropTypes.object.isRequired, + // The reaction content / key / emoji + content: PropTypes.string.isRequired, + // A Set of Martix reaction events for this key + reactionEvents: PropTypes.object.isRequired, + visible: PropTypes.bool.isRequired, + } + + render() { + const Tooltip = sdk.getComponent('elements.Tooltip'); + const { content, reactionEvents, mxEvent, visible } = this.props; + + const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + let tooltipLabel; + if (room) { + const senders = []; + for (const reactionEvent of reactionEvents) { + const { name } = room.getMember(reactionEvent.getSender()); + senders.push(name); + } + const shortName = unicodeToShort(content) || content; + tooltipLabel =
{_t( + "reacted with %(shortName)s", + { + shortName, + }, + { + reactors: () => { + return
+ {senders.join(", ")} +
; + }, + reactedWith: (sub) => { + return
+ {sub} +
; + }, + }, + )}
; + } + + let tooltip; + if (tooltipLabel) { + tooltip = ; + } + + return tooltip; + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f535549232..3a740ea515 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -907,6 +907,7 @@ "Invalid file%(extra)s": "Invalid file%(extra)s", "Error decrypting image": "Error decrypting image", "Error decrypting video": "Error decrypting video", + "reacted with %(shortName)s": "reacted with %(shortName)s", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s changed the room avatar to ", From 059988ff5c1968fd0780f0d000c671d13f285910 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 12:09:47 +0100 Subject: [PATCH 063/273] Extract tooltip styling to a shared class We want to use the same styling with edited tooltip as well, so this extracts the shared bits. --- res/css/_common.scss | 1 - res/css/views/elements/_Tooltip.scss | 13 +++++++++++++ .../views/messages/_ReactionsRowButtonTooltip.scss | 10 ---------- res/themes/dark/css/_dark.scss | 5 +++-- res/themes/light/css/_light.scss | 5 +++-- .../views/messages/ReactionsRowButtonTooltip.js | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index d3cf1921e0..d46f38bddb 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -557,4 +557,3 @@ textarea { .mx_Username_color8 { color: $username-variant8-color; } - diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss index 43ddf6dde5..66e8b5943f 100644 --- a/res/css/views/elements/_Tooltip.scss +++ b/res/css/views/elements/_Tooltip.scss @@ -74,3 +74,16 @@ limitations under the License. animation: mx_fadeout 0.1s forwards; } } + +.mx_Tooltip_timeline { + box-shadow: none; + background-color: $tooltip-timeline-bg-color; + color: $tooltip-timeline-fg-color; + text-align: center; + border: none; + border-radius: 3px; + + .mx_Tooltip_chevron::after { + border-right-color: $tooltip-timeline-bg-color; + } +} diff --git a/res/css/views/messages/_ReactionsRowButtonTooltip.scss b/res/css/views/messages/_ReactionsRowButtonTooltip.scss index 086271e556..95e339144f 100644 --- a/res/css/views/messages/_ReactionsRowButtonTooltip.scss +++ b/res/css/views/messages/_ReactionsRowButtonTooltip.scss @@ -15,18 +15,8 @@ limitations under the License. */ .mx_ReactionsRowButtonTooltip { - box-shadow: none; - background-color: $reaction-row-button-tooltip-bg-color; - color: $reaction-row-button-tooltip-fg-color; - text-align: center; font-size: 8px; padding: 6px; - border: none; - border-radius: 3px; - - .mx_Tooltip_chevron::after { - border-right-color: $reaction-row-button-tooltip-bg-color; - } .mx_ReactionsRowButtonTooltip_reactedWith { opacity: 0.7; diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 251f038f75..bdccf71540 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -156,8 +156,9 @@ $reaction-row-button-border-color: #616b7f; $reaction-row-button-hover-border-color: $header-panel-text-primary-color; $reaction-row-button-selected-bg-color: #1f6954; $reaction-row-button-selected-border-color: $accent-color; -$reaction-row-button-tooltip-bg-color: $tagpanel-bg-color; -$reaction-row-button-tooltip-fg-color: #ffffff; + +$tooltip-timeline-bg-color: $tagpanel-bg-color; +$tooltip-timeline-fg-color: #ffffff; // ***** Mixins! ***** diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 375b4a44b3..d11dfebda3 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -264,8 +264,9 @@ $reaction-row-button-border-color: #e9edf1; $reaction-row-button-hover-border-color: $focus-bg-color; $reaction-row-button-selected-bg-color: #e9fff9; $reaction-row-button-selected-border-color: $accent-color; -$reaction-row-button-tooltip-bg-color: $tagpanel-bg-color; -$reaction-row-button-tooltip-fg-color: #ffffff; + +$tooltip-timeline-bg-color: $tagpanel-bg-color; +$tooltip-timeline-fg-color: #ffffff; // ***** Mixins! ***** diff --git a/src/components/views/messages/ReactionsRowButtonTooltip.js b/src/components/views/messages/ReactionsRowButtonTooltip.js index fc4aed0410..709c20c113 100644 --- a/src/components/views/messages/ReactionsRowButtonTooltip.js +++ b/src/components/views/messages/ReactionsRowButtonTooltip.js @@ -69,7 +69,7 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent { let tooltip; if (tooltipLabel) { tooltip = ; From 603e6b7055a8cbfa12665c403bcf156085731426 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 12:19:02 +0100 Subject: [PATCH 064/273] Adjust edited tooltip to use shared styles --- res/css/views/rooms/_EventTile.scss | 5 +++++ src/components/views/messages/TextualBody.js | 7 +++++-- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 8f67069c82..aa473ec317 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -490,6 +490,11 @@ limitations under the License. } */ +.mx_EventTile_editedTooltip { + font-size: 10px; + padding: 5px 6px; +} + /* end of overrides */ .mx_MatrixChat_useCompactLayout { diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 0ca4711b07..44c807e4e4 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -448,14 +448,17 @@ module.exports = React.createClass({ const Tooltip = sdk.getComponent('elements.Tooltip'); const editEvent = this.props.mxEvent.replacingEvent(); const date = editEvent && formatDate(editEvent.getDate()); - editedTooltip = ; + editedTooltip = ; } return (
{editedTooltip}{`(${_t("Edited")})`}
+ >{editedTooltip}{`(${_t("edited")})`}
); }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3a740ea515..9d54a65eda 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -918,7 +918,7 @@ "Add an Integration": "Add an Integration", "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", "Edited at %(date)s.": "Edited at %(date)s.", - "Edited": "Edited", + "edited": "edited", "Removed or unknown message type": "Removed or unknown message type", "Message removed by %(userId)s": "Message removed by %(userId)s", "Message removed": "Message removed", From f285040e0bf3fe3c14e5cdf3c5596f9f30e2feba Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 13:26:06 +0100 Subject: [PATCH 065/273] check msgtype of original event --- src/utils/EventUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.js index 81aa82057b..68f6d4b14e 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -47,6 +47,6 @@ export function isContentActionable(mxEvent) { export function canEditContent(mxEvent) { return isContentActionable(mxEvent) && - mxEvent.getContent().msgtype === "m.text" && + mxEvent.getOriginalContent().msgtype === "m.text" && mxEvent.getSender() === MatrixClientPeg.get().getUserId(); } From 76ceee0e6ca582875f560772dbf6e3eb736918e2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 13:31:26 +0100 Subject: [PATCH 066/273] silence react warning when showing edited marker, by also giving the body a key --- src/HtmlUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 1032c52e32..97f547ceb4 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -541,8 +541,8 @@ export function bodyToHtml(content, highlights, opts={}) { }); return isDisplayedWithHtml ? - : - { strippedBody }; + : + { strippedBody }; } export function emojifyText(text, addAlt) { From 83019b86710f01bd5738d4af5de6ca2e816fa44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 17 May 2019 13:01:19 +0000 Subject: [PATCH 067/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 85 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 4da12b701d..0e6608fc8f 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -422,9 +422,9 @@ "You must join the room to see its files": "Vous devez rejoindre le salon pour voir ses fichiers", "Reject all %(invitedRooms)s invites": "Rejeter la totalité des %(invitedRooms)s invitations", "Start new chat": "Démarrer une nouvelle discussion", - "Failed to invite": "Echec de l'invitation", - "Failed to invite user": "Echec lors de l'invitation de l'utilisateur", - "Failed to invite the following users to the %(roomName)s room:": "Echec lors de l’invitation des utilisateurs suivants dans le salon %(roomName)s :", + "Failed to invite": "Échec de l’invitation", + "Failed to invite user": "Échec lors de l'invitation de l'utilisateur", + "Failed to invite the following users to the %(roomName)s room:": "Échec de l’invitation des utilisateurs suivants dans le salon %(roomName)s :", "Confirm Removal": "Confirmer la suppression", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Voulez-vous vraiment supprimer cet événement ? Notez que si vous supprimez le changement du nom ou du sujet d’un salon, il est possible que ce changement soit annulé.", "Unknown error": "Erreur inconnue", @@ -1892,5 +1892,82 @@ "Upload %(count)s other files|other": "Envoyer %(count)s autres fichiers", "Upload %(count)s other files|one": "Envoyer %(count)s autre fichier", "Cancel All": "Tout annuler", - "Upload Error": "Erreur d’envoi" + "Upload Error": "Erreur d’envoi", + "A conference call could not be started because the integrations server is not available": "L’appel en téléconférence n’a pas pu être lancé car les intégrations du serveur ne sont pas disponibles", + "The server does not support the room version specified.": "Le serveur ne prend pas en charge la version de salon spécifiée.", + "Name or Matrix ID": "Nom ou identifiant Matrix", + "Email, name or Matrix ID": "E-mail, nom ou identifiant Matrix", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Veuillez confirmer la mise à niveau de ce salon de à .", + "Changes your avatar in this current room only": "Change votre avatar seulement dans le salon actuel", + "Unbans user with given ID": "Révoque le bannissement de l’utilisateur ayant l’identifiant fourni", + "Sends the given message coloured as a rainbow": "Envoie le message coloré aux couleurs de l’arc-en-ciel", + "Sends the given emote coloured as a rainbow": "Envoie l’émoji coloré aux couleurs de l’arc-en-ciel", + "The user's homeserver does not support the version of the room.": "Le serveur d’accueil de l’utilisateur ne prend pas en charge la version de ce salon.", + "Edit messages after they have been sent (refresh to apply changes)": "Éditer les messages après leur envoi (actualiser pour appliquer les changements)", + "React to messages with emoji (refresh to apply changes)": "Réagir aux messages avec des émojis (actualiser pour appliquer les changements)", + "When rooms are upgraded": "Quand les salons sont mis à niveau", + "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Cet appareil ne sauvegarde pas vos clés, mais vous avez une sauvegarde existante que vous pouvez restaurer et joindre.", + "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Connecter cet appareil à la sauvegarde de clés avant de vous déconnecter pour éviter de perdre des clés qui pourraient n’être présentes que sur cet appareil.", + "Connect this device to Key Backup": "Connecter cet appareil à la sauvegarde de clés", + "Backup has an invalid signature from this device": "La sauvegarde a une signature invalide depuis cet appareil", + "this room": "ce salon", + "View older messages in %(roomName)s.": "Voir les messages plus anciens dans %(roomName)s.", + "Joining room …": "Inscription au salon…", + "Loading …": "Chargement…", + "Rejecting invite …": "Rejet de l’invitation…", + "Join the conversation with an account": "Rejoindre la conversation avec un compte", + "Sign Up": "S’inscrire", + "Sign In": "Se connecter", + "You were kicked from %(roomName)s by %(memberName)s": "Vous avez été expulsé(e) de %(roomName)s par %(memberName)s", + "Reason: %(reason)s": "Motif : %(reason)s", + "Forget this room": "Oublier ce salon", + "Re-join": "Revenir", + "You were banned from %(roomName)s by %(memberName)s": "Vous avez été banni(e) de %(roomName)s par %(memberName)s", + "Something went wrong with your invite to %(roomName)s": "Une erreur est survenue avec votre invitation à %(roomName)s", + "%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.": "%(errcode)s a été retourné en essayant de valider votre invitation. Vous pouvez essayer de transmettre cette information à l’administrateur du salon.", + "You can only join it with a working invite.": "Vous ne pouvez le rejoindre qu’avec une invitation fonctionnelle.", + "You can still join it because this is a public room.": "Vous pouvez quand même le rejoindre car c’est un salon public.", + "Join the discussion": "Rejoindre la discussion", + "Try to join anyway": "Essayer de le rejoindre quand même", + "This invite to %(roomName)s wasn't sent to your account": "Cette invitation à %(roomName)s n’a pas été envoyée à votre compte", + "Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.": "Connectez-vous avec un autre compte, demandez une autre invitation ou ajoutez l’adresse e-mail %(email)s à ce compte.", + "Do you want to chat with %(user)s?": "Voulez-vous discuter avec %(user)s ?", + "Do you want to join %(roomName)s?": "Voulez-vous rejoindre %(roomName)s ?", + " invited you": " vous a invité(e)", + "You're previewing %(roomName)s. Want to join it?": "Ceci est un aperçu de %(roomName)s. Voulez-vous le rejoindre ?", + "%(roomName)s can't be previewed. Do you want to join it?": "Vous ne pouvez pas avoir d’aperçu de %(roomName)s. Voulez-vous le rejoindre ?", + "This room doesn't exist. Are you sure you're at the right place?": "Ce salon n’existe pas. Êtes-vous vraiment au bon endroit ?", + "Try again later, or ask a room admin to check if you have access.": "Réessayez plus tard ou demandez à l’administrateur du salon si vous y avez accès.", + "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "%(errcode)s a été retourné en essayant d’accéder au salon. Si vous pensez que vous ne devriez pas voir ce message, veuillez soumettre un rapport d’anomalie.", + "This room has already been upgraded.": "Ce salon a déjà été mis à niveau.", + "Agree or Disagree": "Accepter ou refuser", + "Like or Dislike": "Aimer ou ne pas aimer", + "reacted with %(shortName)s": "ont réagi avec %(shortName)s", + "Edited at %(date)s.": "Édité à %(date)s.", + "edited": "édité", + "Rotate Left": "Tourner à gauche", + "Rotate Right": "Tourner à droite", + "View Servers in Room": "Voir les serveurs dans le salon", + "Use an email address to recover your account": "Utiliser une adresse e-mail pour récupérer votre compte", + "Enter email address (required on this homeserver)": "Saisir l’adresse e-mail (obligatoire sur ce serveur d’accueil)", + "Doesn't look like a valid email address": "Cela ne ressemble pas a une adresse e-mail valide", + "Enter password": "Saisir le mot de passe", + "Password is allowed, but unsafe": "Ce mot de passe est autorisé, mais peu sûr", + "Nice, strong password!": "Bien joué, un mot de passe robuste !", + "Passwords don't match": "Les mots de passe ne correspondent pas", + "Other users can invite you to rooms using your contact details": "D’autres utilisateurs peuvent vous inviter à des salons grâce à vos informations de contact", + "Enter phone number (required on this homeserver)": "Saisir le numéro de téléphone (obligatoire sur ce serveur d’accueil)", + "Doesn't look like a valid phone number": "Cela ne ressemble pas à un numéro de téléphone valide", + "Use letters, numbers, dashes and underscores only": "Utilisez uniquement des lettres, chiffres, traits d’union et tirets bas", + "Enter username": "Saisir le nom d’utilisateur", + "Some characters not allowed": "Certains caractères ne sont pas autorisés", + "Use an email address to recover your account.": "Utilisez une adresse e-mail pour récupérer votre compte.", + "Other users can invite you to rooms using your contact details.": "D’autre utilisateurs peuvent vous inviter à des salons en utilisant vos informations de contact.", + "Error loading Riot": "Erreur lors du chargement de Riot", + "If this is unexpected, please contact your system administrator or technical support representative.": "Si cela est inattendu, veuillez contacter votre administrateur système ou un représentant du support technique.", + "Failed to get autodiscovery configuration from server": "Échec de la découverte automatique de la configuration depuis le serveur", + "Invalid base_url for m.homeserver": "base_url pour m.homeserver non valide", + "Homeserver URL does not appear to be a valid Matrix homeserver": "L’URL du serveur d’accueil ne semble pas être un serveur d’accueil Matrix valide", + "Invalid base_url for m.identity_server": "base_url pour m.identity_server non valide", + "Identity server URL does not appear to be a valid identity server": "L’URL du serveur d’identité ne semble pas être un serveur d’identité valide" } From 9df3face98952831bcdcc489010dcb65e49544f4 Mon Sep 17 00:00:00 2001 From: grrgui Date: Fri, 17 May 2019 13:43:25 +0000 Subject: [PATCH 068/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 0e6608fc8f..47e66d19a6 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -313,7 +313,7 @@ "Unknown room %(roomId)s": "Salon inconnu %(roomId)s", "Unmute": "Activer le son", "Upload avatar": "Télécharger une photo de profil", - "Upload Failed": "Erreur lors de l'envoi", + "Upload Failed": "Échec de l'envoi", "Upload Files": "Télécharger les fichiers", "Upload file": "Envoyer un fichier", "Usage": "Utilisation", From 50ac8076a2aba47d0674fc54741a3c5f9b3ffaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 17 May 2019 13:43:36 +0000 Subject: [PATCH 069/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 47e66d19a6..4fc5e82283 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -313,7 +313,7 @@ "Unknown room %(roomId)s": "Salon inconnu %(roomId)s", "Unmute": "Activer le son", "Upload avatar": "Télécharger une photo de profil", - "Upload Failed": "Échec de l'envoi", + "Upload Failed": "Échec de l’envoi", "Upload Files": "Télécharger les fichiers", "Upload file": "Envoyer un fichier", "Usage": "Utilisation", From 21eda96ddf60dee7ec71f3453e75d16e13ff5743 Mon Sep 17 00:00:00 2001 From: nouts Date: Fri, 17 May 2019 13:44:05 +0000 Subject: [PATCH 070/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 4fc5e82283..a64c13ad0c 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -641,7 +641,7 @@ "Revoke widget access": "Révoquer les accès du widget", "Sets the room topic": "Défini le sujet du salon", "To get started, please pick a username!": "Pour commencer, choisissez un nom d'utilisateur !", - "Unable to create widget.": "Impossible de créer un widget.", + "Unable to create widget.": "Impossible de créer le widget.", "Unbans user with given id": "Révoque le bannissement de l'utilisateur à partir de son identifiant", "You are not in this room.": "Vous n'êtes pas dans ce salon.", "You do not have permission to do that in this room.": "Vous n'avez pas la permission d'effectuer cette action dans ce salon.", From c3f9440f246c10389a7a74434c9e8cd2deb059a7 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 17 May 2019 13:44:47 +0000 Subject: [PATCH 071/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index a64c13ad0c..b39353a719 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1019,7 +1019,7 @@ "Cancel Sending": "Annuler l'envoi", "This Room": "Ce salon", "The Home Server may be too old to support third party networks": "Le serveur d'accueil semble trop ancien pour supporter des réseaux tiers", - "Noisy": "Bruyant", + "Noisy": "Sonore", "Room not found": "Salon non trouvé", "Messages containing my display name": "Messages contenant mon nom affiché", "Messages in one-to-one chats": "Messages dans les discussions directes", From 5fb934c6961dd44985121a6ccfbdd548b7cc0768 Mon Sep 17 00:00:00 2001 From: Hugues De Keyzer Date: Fri, 17 May 2019 13:45:01 +0000 Subject: [PATCH 072/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index b39353a719..5f6297f986 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -716,7 +716,7 @@ "%(names)s and %(count)s others are typing|other": "%(names)s et %(count)s autres écrivent", "Jump to read receipt": "Aller à l'accusé de lecture", "World readable": "Lisible publiquement", - "Guests can join": "Les invités peuvent rejoindre le salon", + "Guests can join": "Accessible aux invités", "To invite users into the room, you must be a": "Pour inviter des utilisateurs dans le salon, vous devez être un(e)", "To configure the room, you must be a": "Pour configurer le salon, vous devez être un(e)", "To kick users, you must be a": "Pour expulser des utilisateurs, vous devez être", From 21106a253996b7c5df2b6c63e2e9a2a26aa61612 Mon Sep 17 00:00:00 2001 From: grrgui Date: Fri, 17 May 2019 13:45:40 +0000 Subject: [PATCH 073/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 5f6297f986..168163a4cd 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -198,7 +198,7 @@ "No devices with registered encryption keys": "Pas d’appareil avec des clés de chiffrement enregistrées", "No more results": "Fin des résultats", "No results": "Pas de résultat", - "unknown error code": "Code d'erreur inconnu", + "unknown error code": "code d'erreur inconnu", "OK": "OK", "Once encryption is enabled for a room it cannot be turned off again (for now)": "Une fois le chiffrement activé dans un salon il ne peut pas être désactivé (pour le moment)", "Only people who have been invited": "Seules les personnes ayant été invitées", From 43c9e6d942609d70bb5a6c22178b320022fcfda8 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 15:04:27 +0100 Subject: [PATCH 074/273] Reactions / editing tooltip tweaks --- res/css/views/elements/_Tooltip.scss | 3 +++ res/css/views/messages/_ReactionsRowButtonTooltip.scss | 9 ++------- res/css/views/rooms/_EventTile.scss | 5 ----- .../views/messages/ReactionsRowButtonTooltip.js | 2 +- src/components/views/messages/TextualBody.js | 4 ++-- src/i18n/strings/en_EN.json | 2 +- 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss index 66e8b5943f..3a6b6fb936 100644 --- a/res/css/views/elements/_Tooltip.scss +++ b/res/css/views/elements/_Tooltip.scss @@ -82,6 +82,9 @@ limitations under the License. text-align: center; border: none; border-radius: 3px; + font-size: 14px; + line-height: 1.2; + padding: 6px 8px; .mx_Tooltip_chevron::after { border-right-color: $tooltip-timeline-bg-color; diff --git a/res/css/views/messages/_ReactionsRowButtonTooltip.scss b/res/css/views/messages/_ReactionsRowButtonTooltip.scss index 95e339144f..cf4219fcec 100644 --- a/res/css/views/messages/_ReactionsRowButtonTooltip.scss +++ b/res/css/views/messages/_ReactionsRowButtonTooltip.scss @@ -14,11 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ReactionsRowButtonTooltip { - font-size: 8px; - padding: 6px; - - .mx_ReactionsRowButtonTooltip_reactedWith { - opacity: 0.7; - } +.mx_ReactionsRowButtonTooltip_reactedWith { + opacity: 0.7; } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index aa473ec317..8f67069c82 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -490,11 +490,6 @@ limitations under the License. } */ -.mx_EventTile_editedTooltip { - font-size: 10px; - padding: 5px 6px; -} - /* end of overrides */ .mx_MatrixChat_useCompactLayout { diff --git a/src/components/views/messages/ReactionsRowButtonTooltip.js b/src/components/views/messages/ReactionsRowButtonTooltip.js index 709c20c113..4f26cea708 100644 --- a/src/components/views/messages/ReactionsRowButtonTooltip.js +++ b/src/components/views/messages/ReactionsRowButtonTooltip.js @@ -69,7 +69,7 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent { let tooltip; if (tooltipLabel) { tooltip = ; diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 44c807e4e4..ea7f634691 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -449,8 +449,8 @@ module.exports = React.createClass({ const editEvent = this.props.mxEvent.replacingEvent(); const date = editEvent && formatDate(editEvent.getDate()); editedTooltip = ; } return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9d54a65eda..86131645cf 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -917,7 +917,7 @@ "Failed to copy": "Failed to copy", "Add an Integration": "Add an Integration", "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", - "Edited at %(date)s.": "Edited at %(date)s.", + "Edited at %(date)s": "Edited at %(date)s", "edited": "edited", "Removed or unknown message type": "Removed or unknown message type", "Message removed by %(userId)s": "Message removed by %(userId)s", From 954f155345f4a50557e060b5936572bb25ddd55c Mon Sep 17 00:00:00 2001 From: Fendo Date: Fri, 17 May 2019 13:39:46 +0000 Subject: [PATCH 075/273] Translated using Weblate (Esperanto) Currently translated at 74.4% (1213 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index bc19f3ba06..b9e1dc56cd 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1392,5 +1392,33 @@ "Okay": "Bone", "Success!": "Sukceso!", "Retry": "Reprovi", - "Set up": "Agordi" + "Set up": "Agordi", + "A conference call could not be started because the integrations server is not available": "Grupa voko ne povis komenciĝi, ĉar la kuniga servilo estas neatingebla", + "Replying With Files": "Respondado kun dosieroj", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Nun ne eblas respondi kun dosiero. Ĉu vi volas alŝuti la dosieron sen respondo?", + "The file '%(fileName)s' failed to upload.": "Malsukcesis alŝuti dosieron «%(fileName)s».", + "The server does not support the room version specified.": "La servilo ne subtenas la donitan ĉambran version.", + "Name or Matrix ID": "Nomo aŭ Matrix-identigilo", + "Email, name or Matrix ID": "Retpoŝtadreso, nomo, aŭ Matrix-identigilo", + "Upgrades a room to a new version": "Gradaltigas ĉambron al nova versio", + "Room upgrade confirmation": "Konfirmo de ĉambra gradaltigo", + "Upgrading a room can be destructive and isn't always necessary.": "Gradaltigo de ĉambro povas esti detrua kaj ne estas ĉiam necesa.", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Gradaltigoj de ĉambroj estas kutime rekomendataj kiam ĉambra versio estas opiniata malstabila. Malstabilaj ĉambraj versioj povas kunhavi erarojn, mankojn de funkcioj, aŭ malsekuraĵojn.", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Ĉambraj gradaltigoj efikas nur sur servil-flanka funkciado de la ĉambro. Se vi havas problemon kun via kliento (Riot), bonvolu raparti problemon per .", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Averto: Gradaltigo de ĉambro ne transmetos ĉiujn ĉambranojn al la nova versio de la ĉambro. Ni afiŝos ligilon al la nova ĉambro en la malnova versio de la ĉambro – ĉambranoj devos tien klaki por aliĝi al la nova ĉambro.", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Bonvolu konfirmi, ke vi certe volas gradaltigi ĉi tiun ĉambron de al .", + "Upgrade": "Gradaltigi", + "Changes your display nickname in the current room only": "Ŝanĝas vian vidigan nomon nur en la nuna ĉambro", + "Changes your avatar in this current room only": "Ŝanĝas vian profilbildon nur en la nuna ĉambro", + "Gets or sets the room topic": "Ekhavas aŭ agordas la temon de la ĉambro", + "This room has no topic.": "Ĉi tiu ĉambro ne havas temon.", + "Sets the room name": "Agordas nomon de la ĉambro", + "Sends the given message coloured as a rainbow": "Sendas la mesaĝon ĉielarke kolorigitan", + "Sends the given emote coloured as a rainbow": "Sendas la mienon ĉielarke kolorigitan", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s gradaltigis ĉi tiun ĉambron.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s publikigis la ĉambron al kiu ajn konas la ligilon.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s necesigis invitojn por aliĝoj al la ĉambro.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s ŝanĝis la aliĝan regulon al %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s permesis al gastoj aliĝi al la ĉambro.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s malpermesis al gastoj aliĝi al la ĉambro." } From f8e95a054fcb8e5196a95447b58bd5a65c38625d Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Fri, 17 May 2019 13:54:51 +0000 Subject: [PATCH 076/273] Translated using Weblate (Finnish) Currently translated at 99.0% (1614 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 515ff416c1..834915922f 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1837,5 +1837,13 @@ "Error loading Riot": "Virhe Riotin lataamisessa", "If this is unexpected, please contact your system administrator or technical support representative.": "Jos et odottanut tätä, ota yhteyttä järjestelmänvalvojaan tai tekniseen tukeen.", "Homeserver URL does not appear to be a valid Matrix homeserver": "Kotipalvelimen osoite ei näytä olevan kelvollinen Matrix-kotipalvelin", - "Identity server URL does not appear to be a valid identity server": "Identiteettipalvelimen osoite ei näytä olevan kelvollinen identiteettipalvelin" + "Identity server URL does not appear to be a valid identity server": "Identiteettipalvelimen osoite ei näytä olevan kelvollinen identiteettipalvelin", + "A conference call could not be started because the integrations server is not available": "Konferenssipuhelua ei voitu aloittaa, koska integraatiopalvelin ei ole käytettävissä", + "When rooms are upgraded": "Kun huoneet päivitetään", + "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Yhdistä tämä laite avainten varmuuskopiointiin ennen kuin kirjaudut ulos, jotta et menetä mahdollisia vain tällä laitteella olevia avaimia.", + "Rejecting invite …": "Hylätään kutsua …", + "You were kicked from %(roomName)s by %(memberName)s": "%(memberName)s poisti sinut huoneesta %(roomName)s", + "Edited at %(date)s.": "Muokattu %(date)s.", + "edited": "muokattu", + "To help us prevent this in future, please send us logs.": "Voit auttaa meitä estämään tämän toistumisen lähettämällä meille lokeja." } From e9e85238c760be0f8ad809ec06fa586c5d57c1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 17 May 2019 13:45:47 +0000 Subject: [PATCH 077/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 168163a4cd..9e5b50558e 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -198,7 +198,7 @@ "No devices with registered encryption keys": "Pas d’appareil avec des clés de chiffrement enregistrées", "No more results": "Fin des résultats", "No results": "Pas de résultat", - "unknown error code": "code d'erreur inconnu", + "unknown error code": "code d’erreur inconnu", "OK": "OK", "Once encryption is enabled for a room it cannot be turned off again (for now)": "Une fois le chiffrement activé dans un salon il ne peut pas être désactivé (pour le moment)", "Only people who have been invited": "Seules les personnes ayant été invitées", From fc58d282d7460bd59ae3a60bc4223558a1a3c3d2 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 17 May 2019 12:08:25 +0000 Subject: [PATCH 078/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1631 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index c758badebb..533ee7a7e8 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1962,5 +1962,12 @@ "Invalid base_url for m.homeserver": "Hibás base_url az m.homeserver -hez", "Homeserver URL does not appear to be a valid Matrix homeserver": "A matrix URL nem tűnik érvényesnek", "Invalid base_url for m.identity_server": "Érvénytelen base_url az m.identity_server -hez", - "Identity server URL does not appear to be a valid identity server": "Az Azonosító szerver URL nem tűnik érvényesnek" + "Identity server URL does not appear to be a valid identity server": "Az Azonosító szerver URL nem tűnik érvényesnek", + "A conference call could not be started because the integrations server is not available": "A konferencia hívást nem lehet elkezdeni mert az integrációs szerver nem érhető el", + "Name or Matrix ID": "Név vagy Matrix azon.", + "Email, name or Matrix ID": "E-mail, név vagy Matrix azon.", + "Unbans user with given ID": "Visszaengedi a megadott azonosítójú felhasználót", + "reacted with %(shortName)s": "ezzel reagált: %(shortName)s", + "Edited at %(date)s.": "Szerkesztve ekkor: %(date)s.", + "edited": "szerkesztve" } From 9a3752c5713e2b05bfa9f9ac68b7b8f78e52971f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 15:30:07 +0100 Subject: [PATCH 079/273] show message editor in textual body instead of replacing event tile --- src/components/structures/MessagePanel.js | 7 ++----- src/components/views/messages/MessageEvent.js | 1 + src/components/views/messages/TextualBody.js | 5 +++++ src/components/views/rooms/EventTile.js | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index dbaab57adf..001097f846 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -450,14 +450,10 @@ module.exports = React.createClass({ _getTilesForEvent: function(prevEvent, mxEv, last) { const EventTile = sdk.getComponent('rooms.EventTile'); - const MessageEditor = sdk.getComponent('elements.MessageEditor'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const ret = []; - if (this.props.editEvent && this.props.editEvent.getId() === mxEv.getId()) { - return []; - } - + const isEditing = this.props.editEvent && this.props.editEvent.getId() === mxEv.getId(); // is this a continuation of the previous message? let continuation = false; @@ -527,6 +523,7 @@ module.exports = React.createClass({ continuation={continuation} isRedacted={mxEv.isRedacted()} replacingEventId={mxEv.replacingEventId()} + isEditing={isEditing} onHeightChanged={this._onHeightChanged} readReceipts={readReceipts} readReceiptMap={this._readReceiptMap} diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index 357da1cd10..8c90ec5a46 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -90,6 +90,7 @@ module.exports = React.createClass({ tileShape={this.props.tileShape} maxImageHeight={this.props.maxImageHeight} replacingEventId={this.props.replacingEventId} + isEditing={this.props.isEditing} onHeightChanged={this.props.onHeightChanged} />; }, }); diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 44c807e4e4..c4a60c3bab 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -148,6 +148,7 @@ module.exports = React.createClass({ nextProps.replacingEventId !== this.props.replacingEventId || nextProps.highlightLink !== this.props.highlightLink || nextProps.showUrlPreview !== this.props.showUrlPreview || + nextProps.isEditing !== this.props.isEditing || nextState.links !== this.state.links || nextState.editedMarkerHovered !== this.state.editedMarkerHovered || nextState.widgetHidden !== this.state.widgetHidden); @@ -463,6 +464,10 @@ module.exports = React.createClass({ }, render: function() { + if (this.props.isEditing) { + const MessageEditor = sdk.getComponent('elements.MessageEditor'); + return ; + } const EmojiText = sdk.getComponent('elements.EmojiText'); const mxEvent = this.props.mxEvent; const content = mxEvent.getContent(); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index f38e3c3946..9977d10395 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -780,6 +780,7 @@ module.exports = withMatrixClient(React.createClass({ Date: Fri, 17 May 2019 15:31:09 +0100 Subject: [PATCH 080/273] update design of editor to look as close to original tile (and design) the buttons below the composer are overlayed onto the previous event. In case of the last event, for now we make them not overflow, but make the tile grow. The design says it should overlay on the main composer for the last event tile, postponing that for a bit though as not sure what is the best way to do that. --- res/css/views/elements/_MessageEditor.scss | 19 +++++++++++++++---- res/css/views/rooms/_EventTile.scss | 4 ++++ src/components/views/rooms/EventTile.js | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index ec6d903753..15b342b436 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -16,14 +16,14 @@ limitations under the License. .mx_MessageEditor { border-radius: 4px; - background-color: $header-panel-bg-color; - padding: 11px 13px 7px 56px; + padding: 3px; + margin: -7px -7px -5px -7px; // no idea why this is working .mx_MessageEditor_editor { border-radius: 4px; border: solid 1px #e9edf1; background-color: #ffffff; - padding: 10px; + padding: 3px; white-space: pre-wrap; word-wrap: break-word; outline: none; @@ -49,7 +49,14 @@ limitations under the License. display: flex; flex-direction: row; justify-content: end; - padding: 5px 0; + padding: 5px; + position: absolute; + left: 0; + background: $header-panel-bg-color; + z-index: 100; + right: 0; + margin: 0 -110px 0 0; + padding-right: 108px; .mx_AccessibleButton { margin-left: 5px; @@ -62,3 +69,7 @@ limitations under the License. height: 0; } } + +.mx_EventTile_last .mx_MessageEditor_buttons { + position: static; +} diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index aa473ec317..93d97b2913 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -43,6 +43,10 @@ limitations under the License. padding-top: 0px ! important; } +.mx_EventTile_isEditing { + background-color: $header-panel-bg-color; +} + .mx_EventTile .mx_SenderProfile { color: $primary-fg-color; font-size: 14px; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 9977d10395..a4a9004041 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -540,6 +540,7 @@ module.exports = withMatrixClient(React.createClass({ const classes = classNames({ mx_EventTile: true, + mx_EventTile_isEditing: this.props.isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, mx_EventTile_encrypting: this.props.eventSendStatus === 'encrypting', From 62b8973e720531dba9fea1b714e739479280ee04 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 15:35:14 +0100 Subject: [PATCH 081/273] cancel the edit when pressing escape --- src/components/views/elements/MessageEditor.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index c18d1f56fd..0c249d067b 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -107,10 +107,12 @@ export default class MessageEditor extends React.Component { } else if (event.key === "Enter") { this._sendEdit(); event.preventDefault(); + } else if (event.key === "Escape") { + this._cancelEdit(); } } - _onCancelClicked = () => { + _cancelEdit = () => { dis.dispatch({action: "edit_event", event: null}); } @@ -185,7 +187,7 @@ export default class MessageEditor extends React.Component { ref={ref => this._editorRef = ref} >
- {_t("Cancel")} + {_t("Cancel")} {_t("Save")}
; From 578a183f49a9f3fe239f19bfc6a5b50d6e4426ca Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 15:35:28 +0100 Subject: [PATCH 082/273] hide the action bar while editing --- src/components/views/rooms/EventTile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index a4a9004041..39e830a228 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -618,14 +618,14 @@ module.exports = withMatrixClient(React.createClass({ } const MessageActionBar = sdk.getComponent('messages.MessageActionBar'); - const actionBar = ; + /> : undefined; const timestamp = this.props.mxEvent.getTs() ? : null; From 26c618dfb649ef7d5a99d98f2b59ec858810ac35 Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Fri, 17 May 2019 14:12:02 +0000 Subject: [PATCH 083/273] Translated using Weblate (Finnish) Currently translated at 99.1% (1617 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 834915922f..3958a8ae60 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -736,8 +736,8 @@ "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", "were unbanned %(count)s times|other": "porttikiellot poistettiin %(count)s kertaa", "And %(count)s more...|other": "Ja %(count)s lisää...", - "Matrix ID": "Matrix ID", - "Matrix Room ID": "Matrix huonetunniste", + "Matrix ID": "Matrix-tunnus", + "Matrix Room ID": "Matrix-huonetunnus", "email address": "sähköpostiosoite", "Try using one of the following valid address types: %(validTypesList)s.": "Kokeile käyttää yhtä näistä kelvollisista osoitetyypeistä: %(validTypesList)s.", "You have entered an invalid address.": "Olet syöttänyt virheellisen sähköpostiosoitteen.", @@ -1345,7 +1345,7 @@ "We encountered an error trying to restore your previous session.": "Törmäsimme ongelmaan yrittäessämme palauttaa edellistä istuntoasi.", "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Jos olet aikaisemmin käyttänyt uudempaa versiota Riotista, istuntosi voi olla epäyhteensopiva tämän version kanssa. Sulje tämä ikkuna ja yritä uudemman version kanssa.", "The platform you're on": "Alusta, jolla olet", - "Whether or not you're logged in (we don't record your username)": "Riippumatta siitä oletko kirjautunut sisään (emme tallenna käyttäjätunnustasi)", + "Whether or not you're logged in (we don't record your username)": "Riippumatta siitä oletko kirjautunut sisään (emme tallenna käyttäjänimeäsi)", "Whether or not you're using the Richtext mode of the Rich Text Editor": "Riippumatta siitä, käytätkö muotoillun tekstin tilaa muotoilueditorissa", "Your User Agent": "Selaintunnisteesi", "The information being sent to us to help make Riot.im better includes:": "Tietoihin, jota lähetetään Riot.im:ään palvelun parantamiseksi, sisältyy:", @@ -1438,7 +1438,7 @@ "For help with using Riot, click here.": "Saadaksesi apua Riotin käyttämisessä, klikkaa tästä.", "For help with using Riot, click here or start a chat with our bot using the button below.": "Saadaksesi apua Riotin käytössä, klikkaa tästä tai aloita keskustelu bottimme kanssa alla olevasta painikkeesta.", "Bug reporting": "Virheiden raportointi", - "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Jos olet ilmoittanut virheestä Githubin kautta, debug-lokit voivat auttaa meitä ongelman jäljittämisessä. Debug-lokit sisältävät ohjelman käyttödataa sisältäen käyttäjätunnuksen, vierailemiesi huoneiden tai ryhmien ID:t tai aliakset ja muiden käyttäjien käyttäjätunnukset. Debug-lokit eivät sisällä viestejä.", + "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Jos olet ilmoittanut virheestä Githubin kautta, debug-lokit voivat auttaa meitä ongelman jäljittämisessä. Debug-lokit sisältävät sovelluksen käyttödataa sisältäen käyttäjänimen, vierailemiesi huoneiden tai ryhmien tunnukset tai aliakset ja muiden käyttäjien käyttäjänimet. Debug-lokit eivät sisällä viestejä.", "Autocomplete delay (ms)": "Automaattisen täydennyksen viive (ms)", "To link to this room, please add an alias.": "Lisää alias linkittääksesi tähän huoneeseen.", "Ignored users": "Hiljennetyt käyttäjät", @@ -1627,7 +1627,7 @@ "Use an email address to recover your account. Other users can invite you to rooms using your contact details.": "Käytä sähköpostia tilisi palauttamiseen. Muut käyttäjät voivat kutsua sinut huoneisiin yhteystiedoillasi.", "Other servers": "Muut palvelimet", "Enter custom server URLs What does this mean?": "Syötä mukautettujen palvelinten osoitteet. Mitä tämä tarkoittaa?", - "Free": "Vapaa", + "Free": "Ilmainen", "Join millions for free on the largest public server": "Liity ilmaiseksi miljoonien joukkoon suurimmalla julkisella palvelimella", "Premium": "Premium", "Premium hosting for organisations Learn more": "Premium-ylläpitoa organisaatioille. Lue lisää", @@ -1845,5 +1845,9 @@ "You were kicked from %(roomName)s by %(memberName)s": "%(memberName)s poisti sinut huoneesta %(roomName)s", "Edited at %(date)s.": "Muokattu %(date)s.", "edited": "muokattu", - "To help us prevent this in future, please send us logs.": "Voit auttaa meitä estämään tämän toistumisen lähettämällä meille lokeja." + "To help us prevent this in future, please send us logs.": "Voit auttaa meitä estämään tämän toistumisen lähettämällä meille lokeja.", + "Name or Matrix ID": "Nimi tai Matrix-tunnus", + "Email, name or Matrix ID": "Sähköposti, nimi tai Matrix-tunnus", + "Edited at %(date)s": "Muokattu %(date)s", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Tiedosto on liian iso ladattavaksi. Tiedostojen kokoraja on %(limit)s mutta tämä tiedosto on %(sizeOfThisFile)s." } From 2544decab2af9b3d40be58828c0b1a032fef802b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:00:08 +0100 Subject: [PATCH 084/273] fix chrome not right aligning buttons --- res/css/views/elements/_MessageEditor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index 15b342b436..cfa62e000f 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -48,7 +48,7 @@ limitations under the License. .mx_MessageEditor_buttons { display: flex; flex-direction: row; - justify-content: end; + justify-content: flex-end; padding: 5px; position: absolute; left: 0; From 81245e9c05bb2dd0c011ab68442ec1c9195199a6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:00:22 +0100 Subject: [PATCH 085/273] cull editor height to 200px --- res/css/views/elements/_MessageEditor.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index cfa62e000f..e48e93aa63 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -27,6 +27,8 @@ limitations under the License. white-space: pre-wrap; word-wrap: break-word; outline: none; + max-height: 200px; + overflow-x: auto; span { display: inline-block; From cf8189ed4371650293e0822db2950b2c686e08a6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:00:39 +0100 Subject: [PATCH 086/273] align buttons perfectly with editor edge --- res/css/views/elements/_MessageEditor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index e48e93aa63..d537584f1c 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -58,7 +58,7 @@ limitations under the License. z-index: 100; right: 0; margin: 0 -110px 0 0; - padding-right: 108px; + padding-right: 107px; .mx_AccessibleButton { margin-left: 5px; From aeea4ee83a4ef4d196327bfb456bc980421b499a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:00:58 +0100 Subject: [PATCH 087/273] a bit more horizontal padding for the editor --- res/css/views/elements/_MessageEditor.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index d537584f1c..6a5f25532f 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -17,13 +17,13 @@ limitations under the License. .mx_MessageEditor { border-radius: 4px; padding: 3px; - margin: -7px -7px -5px -7px; // no idea why this is working + margin: -7px -10px -5px -10px; // from fiddling around in inspector .mx_MessageEditor_editor { border-radius: 4px; border: solid 1px #e9edf1; background-color: #ffffff; - padding: 3px; + padding: 3px 6px; white-space: pre-wrap; word-wrap: break-word; outline: none; From 5adae63555f31496494aeab08e054fe4edebca39 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:01:30 +0100 Subject: [PATCH 088/273] don't apply formatting to body when showing editor in TextualBody it throws --- src/components/views/messages/TextualBody.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index c4a60c3bab..bd37f98360 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -89,7 +89,9 @@ module.exports = React.createClass({ componentDidMount: function() { this._unmounted = false; - this._applyFormatting(); + if (!this.props.isEditing) { + this._applyFormatting(); + } }, _applyFormatting() { @@ -128,11 +130,13 @@ module.exports = React.createClass({ }, componentDidUpdate: function(prevProps) { - const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId; - if (messageWasEdited) { - this._applyFormatting(); + if (!this.props.isEditing) { + const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId; + if (messageWasEdited) { + this._applyFormatting(); + } + this.calculateUrlPreview(); } - this.calculateUrlPreview(); }, componentWillUnmount: function() { From f9462d1012d46b6f7ec0b1c2654b2c018a7ebb10 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:01:52 +0100 Subject: [PATCH 089/273] hide timestamp while editing --- res/css/views/rooms/_EventTile.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 93d97b2913..dfc560e670 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -76,6 +76,10 @@ limitations under the License. } } +.mx_EventTile_isEditing .mx_MessageTimestamp { + visibility: hidden !important; +} + .mx_EventTile .mx_MessageTimestamp { display: block; visibility: hidden; From aef9323f216ed74dac2aa17bbed519c7c1bb554e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:10:11 +0100 Subject: [PATCH 090/273] explain negative margin --- res/css/views/elements/_MessageEditor.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index 6a5f25532f..8f3fdfb9ef 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -17,7 +17,10 @@ limitations under the License. .mx_MessageEditor { border-radius: 4px; padding: 3px; - margin: -7px -10px -5px -10px; // from fiddling around in inspector + // this is to try not make the text move but still have some + // padding around and in the editor. + // Actual values from fiddling around in inspector + margin: -7px -10px -5px -10px; .mx_MessageEditor_editor { border-radius: 4px; From 8ce8ca18ff2deaeb8362f3cbc7cbb541cff57359 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:10:21 +0100 Subject: [PATCH 091/273] theme-ify! --- res/css/views/elements/_MessageEditor.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index 8f3fdfb9ef..40bc132edb 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -24,8 +24,8 @@ limitations under the License. .mx_MessageEditor_editor { border-radius: 4px; - border: solid 1px #e9edf1; - background-color: #ffffff; + border: solid 1px $primary-hairline-color; + background-color: $primary-bg-color; padding: 3px 6px; white-space: pre-wrap; word-wrap: break-word; From d81ab2464bb5cd1d52996a92f5d5a975a91cf5c7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 16:15:06 +0100 Subject: [PATCH 092/273] better button alignment also fix sticking out at the right side when not overlaying --- res/css/views/elements/_MessageEditor.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index 40bc132edb..cc5649a224 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -61,7 +61,7 @@ limitations under the License. z-index: 100; right: 0; margin: 0 -110px 0 0; - padding-right: 107px; + padding-right: 104px; .mx_AccessibleButton { margin-left: 5px; @@ -77,4 +77,5 @@ limitations under the License. .mx_EventTile_last .mx_MessageEditor_buttons { position: static; + margin-right: -103px; } From 3a405701a31f671cbcb6f96c01cc64b48480882f Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 2 May 2019 15:11:06 +0100 Subject: [PATCH 093/273] Debug: Show all events --- res/css/_components.scss | 1 + res/css/views/messages/_ViewSourceEvent.scss | 50 ++++++++++++++ src/components/structures/MessagePanel.js | 5 ++ .../views/messages/ViewSourceEvent.js | 67 +++++++++++++++++++ src/components/views/rooms/EventTile.js | 5 +- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 6 ++ 7 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 res/css/views/messages/_ViewSourceEvent.scss create mode 100644 src/components/views/messages/ViewSourceEvent.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 9823b4ac3d..4b8b687146 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -124,6 +124,7 @@ @import "./views/messages/_SenderProfile.scss"; @import "./views/messages/_TextualEvent.scss"; @import "./views/messages/_UnknownBody.scss"; +@import "./views/messages/_ViewSourceEvent.scss"; @import "./views/room_settings/_AliasSettings.scss"; @import "./views/room_settings/_ColorSettings.scss"; @import "./views/rooms/_AppsDrawer.scss"; diff --git a/res/css/views/messages/_ViewSourceEvent.scss b/res/css/views/messages/_ViewSourceEvent.scss new file mode 100644 index 0000000000..a15924e759 --- /dev/null +++ b/res/css/views/messages/_ViewSourceEvent.scss @@ -0,0 +1,50 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_EventTile_content.mx_ViewSourceEvent { + display: flex; + opacity: 0.6; + font-size: 12px; + + pre, code { + flex: 1; + } + + pre { + line-height: 1.2; + margin: 3.5px 0; + } + + .mx_ViewSourceEvent_toggle { + width: 12px; + mask-repeat: no-repeat; + mask-position: 0 center; + mask-size: auto 12px; + visibility: hidden; + background-color: $accent-color; + mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); + } + + &.mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle { + mask-position: 0 bottom; + margin-bottom: 7px; + mask-image: url('$(res)/img/feather-customised/widget/minimise.svg'); + } + + &:hover .mx_ViewSourceEvent_toggle { + visibility: visible; + } +} diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index dbaab57adf..562d067a7b 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -24,6 +24,7 @@ import {wantsDateSeparator} from '../../DateUtils'; import sdk from '../../index'; import MatrixClientPeg from '../../MatrixClientPeg'; +import SettingsStore from '../../settings/SettingsStore'; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -248,6 +249,10 @@ module.exports = React.createClass({ return false; // ignored = no show (only happens if the ignore happens after an event was received) } + if (SettingsStore.isFeatureEnabled("showHiddenEventsInTimeline")) { + return true; + } + const EventTile = sdk.getComponent('rooms.EventTile'); if (!EventTile.haveTileForEvent(mxEv)) { return false; // no tile = no show diff --git a/src/components/views/messages/ViewSourceEvent.js b/src/components/views/messages/ViewSourceEvent.js new file mode 100644 index 0000000000..717a4b0f05 --- /dev/null +++ b/src/components/views/messages/ViewSourceEvent.js @@ -0,0 +1,67 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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 classNames from 'classnames'; + +export default class ViewSourceEvent extends React.PureComponent { + static propTypes = { + /* the MatrixEvent to show */ + mxEvent: PropTypes.object.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + expanded: false, + }; + } + + onToggle = (ev) => { + ev.preventDefault(); + const { expanded } = this.state; + this.setState({ + expanded: !expanded, + }); + } + + render() { + const { mxEvent } = this.props; + const { expanded } = this.state; + + let content; + if (expanded) { + content =
{JSON.stringify(mxEvent, null, 4)}
; + } else { + content = {`{ "type": ${mxEvent.getType()} }`}; + } + + const classes = classNames("mx_ViewSourceEvent mx_EventTile_content", { + mx_ViewSourceEvent_expanded: expanded, + }); + + return + {content} + + ; + } + } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index f38e3c3946..91699c0c99 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -520,7 +520,10 @@ module.exports = withMatrixClient(React.createClass({ eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType != 'm.room.create' ); - const tileHandler = getHandlerTile(this.props.mxEvent); + let tileHandler = getHandlerTile(this.props.mxEvent); + if (!tileHandler && SettingsStore.isFeatureEnabled("showHiddenEventsInTimeline")) { + tileHandler = "messages.ViewSourceEvent"; + } // This shouldn't happen: the caller should check we support this type // before trying to instantiate us if (!tileHandler) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 86131645cf..067c2bdeef 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -302,6 +302,7 @@ "Render simple counters in room header": "Render simple counters in room header", "Edit messages after they have been sent (refresh to apply changes)": "Edit messages after they have been sent (refresh to apply changes)", "React to messages with emoji (refresh to apply changes)": "React to messages with emoji (refresh to apply changes)", + "Show hidden events in timeline": "Show hidden events in timeline", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 429030d862..5db8599112 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -130,6 +130,12 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "showHiddenEventsInTimeline": { + isFeature: true, + displayName: _td("Show hidden events in timeline"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 45cdf880b1305a161b73c072a8cdd3acfba90124 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 17:43:08 +0100 Subject: [PATCH 094/273] Change setting style --- src/components/structures/MessagePanel.js | 2 +- src/components/views/rooms/EventTile.js | 2 +- .../views/settings/tabs/user/LabsUserSettingsTab.js | 1 + src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.js | 11 +++++------ 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 562d067a7b..4d86305753 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -249,7 +249,7 @@ module.exports = React.createClass({ return false; // ignored = no show (only happens if the ignore happens after an event was received) } - if (SettingsStore.isFeatureEnabled("showHiddenEventsInTimeline")) { + if (SettingsStore.getValue("showHiddenEventsInTimeline")) { return true; } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 91699c0c99..8269717cc7 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -521,7 +521,7 @@ module.exports = withMatrixClient(React.createClass({ ); let tileHandler = getHandlerTile(this.props.mxEvent); - if (!tileHandler && SettingsStore.isFeatureEnabled("showHiddenEventsInTimeline")) { + if (!tileHandler && SettingsStore.getValue("showHiddenEventsInTimeline")) { tileHandler = "messages.ViewSourceEvent"; } // This shouldn't happen: the caller should check we support this type diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js index c2e62044a3..d272d74d29 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js @@ -52,6 +52,7 @@ export default class LabsUserSettingsTab extends React.Component {
{flags} +
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 067c2bdeef..b886e70422 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -302,7 +302,6 @@ "Render simple counters in room header": "Render simple counters in room header", "Edit messages after they have been sent (refresh to apply changes)": "Edit messages after they have been sent (refresh to apply changes)", "React to messages with emoji (refresh to apply changes)": "React to messages with emoji (refresh to apply changes)", - "Show hidden events in timeline": "Show hidden events in timeline", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", @@ -334,6 +333,7 @@ "Prompt before sending invites to potentially invalid matrix IDs": "Prompt before sending invites to potentially invalid matrix IDs", "Show developer tools": "Show developer tools", "Order rooms in the room list by most important first instead of most recent": "Order rooms in the room list by most important first instead of most recent", + "Show hidden events in timeline": "Show hidden events in timeline", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 5db8599112..116526b63a 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -130,12 +130,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "showHiddenEventsInTimeline": { - isFeature: true, - displayName: _td("Show hidden events in timeline"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), @@ -374,4 +368,9 @@ export const SETTINGS = { displayName: _td('Order rooms in the room list by most important first instead of most recent'), default: true, }, + "showHiddenEventsInTimeline": { + displayName: _td("Show hidden events in timeline"), + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + default: false, + }, }; From 5144907983bbd781d03d28b71dd6ea51942c2b97 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 17 May 2019 17:44:11 +0100 Subject: [PATCH 095/273] Fix indent --- src/components/views/messages/ViewSourceEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/ViewSourceEvent.js b/src/components/views/messages/ViewSourceEvent.js index 717a4b0f05..62cf45fb6e 100644 --- a/src/components/views/messages/ViewSourceEvent.js +++ b/src/components/views/messages/ViewSourceEvent.js @@ -63,5 +63,5 @@ export default class ViewSourceEvent extends React.PureComponent { onClick={this.onToggle} /> ; - } } +} From 47add752780fd462eefdfb2601923487dc421d6d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 17 May 2019 15:25:17 -0600 Subject: [PATCH 096/273] Add class to hide focus highlight We use tabIndex to make elements selectable and therefore focused by screen readers. Doing this draws a blue border (in chrome at least) around the element - in some cases, we don't want this. --- res/css/_common.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index d46f38bddb..ba239b8ca8 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -110,6 +110,10 @@ textarea { color: $primary-fg-color; } +.mx_HiddenFocusable { + outline: none; +} + // .mx_textinput is a container for a text input // + some other controls like buttons, ... // it has the appearance of a text box so the controls From f1aa2875e1e9bf6c7a8d87f93b8f8f050568b0d6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 17 May 2019 15:25:59 -0600 Subject: [PATCH 097/273] Hide avatars from screen readers by default To avoid having them read out the user's ID --- src/components/views/avatars/BaseAvatar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 47de7c9dc4..af84b7fd81 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -189,7 +189,7 @@ module.exports = React.createClass({ const imgNode = ( + width={width} height={height} aria-hidden="true" /> ); if (onClick != null) { return ( From c5757d8303bda840ac417c8bdaedfab65d3347e6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 17 May 2019 15:28:12 -0600 Subject: [PATCH 098/273] Support CTRL+I for opening TopLeftMenu --- src/components/structures/LoggedInView.js | 8 +++++ .../structures/TopLeftMenuButton.js | 36 ++++++++++++++++--- .../views/elements/AccessibleButton.js | 5 +++ src/i18n/strings/en_EN.json | 1 + 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 4771c6f487..130a3bf055 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -322,6 +322,14 @@ const LoggedInView = React.createClass({ handled = true; } break; + case KeyCode.KEY_I: + if (ctrlCmdOnly) { + dis.dispatch({ + action: 'toggle_top_left_menu', + }); + handled = true; + } + break; } if (handled) { diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js index b68d3a95a0..36cd359b8a 100644 --- a/src/components/structures/TopLeftMenuButton.js +++ b/src/components/structures/TopLeftMenuButton.js @@ -23,6 +23,7 @@ import BaseAvatar from '../views/avatars/BaseAvatar'; import MatrixClientPeg from '../../MatrixClientPeg'; import Avatar from '../../Avatar'; import { _t } from '../../languageHandler'; +import dis from "../../dispatcher"; const AVATAR_SIZE = 28; @@ -37,6 +38,7 @@ export default class TopLeftMenuButton extends React.Component { super(); this.state = { menuDisplayed: false, + menuFunctions: null, // should be { close: fn } profileInfo: null, }; @@ -59,6 +61,8 @@ export default class TopLeftMenuButton extends React.Component { } async componentDidMount() { + this._dispatcherRef = dis.register(this.onAction); + try { const profileInfo = await this._getProfileInfo(); this.setState({profileInfo}); @@ -68,6 +72,17 @@ export default class TopLeftMenuButton extends React.Component { } } + componentWillUnmount() { + dis.unregister(this._dispatcherRef); + } + + onAction = (payload) => { + // For accessibility + if (payload.action === "toggle_top_left_menu") { + if (this._buttonRef) this._buttonRef.click(); + } + }; + _getDisplayName() { if (MatrixClientPeg.get().isGuest()) { return _t("Guest"); @@ -88,7 +103,13 @@ export default class TopLeftMenuButton extends React.Component { } return ( - + this._buttonRef = r} + aria-label={_t("Your profile")} + > { nameElement } - + ); } @@ -107,20 +128,25 @@ export default class TopLeftMenuButton extends React.Component { e.preventDefault(); e.stopPropagation(); + if (this.state.menuDisplayed && this.state.menuFunctions) { + this.state.menuFunctions.close(); + return; + } + const elementRect = e.currentTarget.getBoundingClientRect(); const x = elementRect.left; const y = elementRect.top + elementRect.height; - ContextualMenu.createMenu(TopLeftMenu, { + const menuFunctions = ContextualMenu.createMenu(TopLeftMenu, { chevronFace: "none", left: x, top: y, userId: MatrixClientPeg.get().getUserId(), displayName: this._getDisplayName(), onFinished: () => { - this.setState({ menuDisplayed: false }); + this.setState({ menuDisplayed: false, menuFunctions: null }); }, }); - this.setState({ menuDisplayed: true }); + this.setState({ menuDisplayed: true, menuFunctions }); } } diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index 1c39ba4f49..06c440c54e 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.js @@ -63,6 +63,10 @@ export default function AccessibleButton(props) { }; } + // Pass through the ref - used for keyboard shortcut access to some buttons + restProps.ref = restProps.inputRef; + delete restProps.inputRef; + restProps.tabIndex = restProps.tabIndex || "0"; restProps.role = "button"; restProps.className = (restProps.className ? restProps.className + " " : "") + @@ -89,6 +93,7 @@ export default function AccessibleButton(props) { */ AccessibleButton.propTypes = { children: PropTypes.node, + inputRef: PropTypes.func, element: PropTypes.string, onClick: PropTypes.func.isRequired, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 86131645cf..2af0af2795 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1493,6 +1493,7 @@ "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", "Failed to load timeline position": "Failed to load timeline position", "Guest": "Guest", + "Your profile": "Your profile", "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s", "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", From 2a187810fd66d7d45bcccf0f8fb64691fd0578c6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 17 May 2019 15:32:03 -0600 Subject: [PATCH 099/273] Restructure TopLeftMenu for accessibility and autofocus it We use a trick with refs to automatically focus the element, also making use of mx_HiddenFocusable to hide the unnecessary outline. The menu itself has been restructured to hide some elements from screen readers (reduce noise) and to have a single unordered list. Screen readers mention when the user "enters" a list, and each item was previously saying "enter list " when it should have just been "". By focusing automatically, the keyboard can be used to go up/down the menu as may be expected by keyboard users. --- .../views/context_menus/TopLeftMenu.js | 43 +++++++++++-------- src/utils/Accessibility.js | 19 ++++++++ 2 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 src/utils/Accessibility.js diff --git a/src/components/views/context_menus/TopLeftMenu.js b/src/components/views/context_menus/TopLeftMenu.js index 278c879404..14e93044f4 100644 --- a/src/components/views/context_menus/TopLeftMenu.js +++ b/src/components/views/context_menus/TopLeftMenu.js @@ -23,6 +23,7 @@ import Modal from "../../../Modal"; import SdkConfig from '../../../SdkConfig'; import { getHostingLink } from '../../../utils/HostingLink'; import MatrixClientPeg from '../../../MatrixClientPeg'; +import {focusCapturedRef} from "../../../utils/Accessibility"; export class TopLeftMenu extends React.Component { static propTypes = { @@ -61,44 +62,48 @@ export class TopLeftMenu extends React.Component { {_t( "Upgrade to your own domain", {}, { - a: sub => {sub}, + a: sub => {sub}, }, )} - +
; } - let homePageSection = null; + let homePageItem = null; if (this.hasHomePage()) { - homePageSection =
    -
  • {_t("Home")}
  • -
; + homePageItem =
  • + {_t("Home")} +
  • ; } - let signInOutSection; + let signInOutItem; if (isGuest) { - signInOutSection =
      -
    • {_t("Sign in")}
    • -
    ; + signInOutItem =
  • + {_t("Sign in")} +
  • ; } else { - signInOutSection =
      -
    • {_t("Sign out")}
    • -
    ; + signInOutItem =
  • + {_t("Sign out")} +
  • ; } - return
    -
    + const settingsItem =
  • + {_t("Settings")} +
  • ; + + return
    +
    {this.props.displayName}
    -
    {this.props.userId}
    +
    {this.props.userId}
    {hostingSignup}
    - {homePageSection}
      -
    • {_t("Settings")}
    • + {homePageItem} + {settingsItem} + {signInOutItem}
    - {signInOutSection}
    ; } diff --git a/src/utils/Accessibility.js b/src/utils/Accessibility.js new file mode 100644 index 0000000000..dbdfeec7df --- /dev/null +++ b/src/utils/Accessibility.js @@ -0,0 +1,19 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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. +*/ + +export function focusCapturedRef(ref) { + if (ref) ref.focus(); +} \ No newline at end of file From 332f716ce4433c5b11abdf83495abb1bc4b89751 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 17 May 2019 15:36:28 -0600 Subject: [PATCH 100/273] The linter will be the death of me --- src/utils/Accessibility.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Accessibility.js b/src/utils/Accessibility.js index dbdfeec7df..8c6090671d 100644 --- a/src/utils/Accessibility.js +++ b/src/utils/Accessibility.js @@ -16,4 +16,4 @@ limitations under the License. export function focusCapturedRef(ref) { if (ref) ref.focus(); -} \ No newline at end of file +} From bad372a13f2f04d0d983ffde26bbfd4078c54542 Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Fri, 17 May 2019 14:42:29 +0000 Subject: [PATCH 101/273] Translated using Weblate (Finnish) Currently translated at 99.2% (1618 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 3958a8ae60..8fd5cbc115 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -37,7 +37,7 @@ "Default Device": "Oletuslaite", "Microphone": "Mikrofoni", "Camera": "Kamera", - "Advanced": "Kehittyneet", + "Advanced": "Edistynyt", "Algorithm": "Algoritmi", "Hide removed messages": "Piilota poistetut viestit", "Always show message timestamps": "Näytä aina viestien aikaleimat", @@ -70,7 +70,7 @@ "and %(count)s others...|one": "ja yksi muu...", "Ban": "Anna porttikielto", "Banned users": "Porttikiellon saanneet käyttäjät", - "Bans user with given id": "Antaa porttikiellon käyttäjälle jolla on annettu tunniste", + "Bans user with given id": "Antaa porttikiellon tunnuksen mukaiselle käyttäjälle", "Bulk Options": "Bulkkiasetukset", "Changes your display nickname": "Muuttaa näyttönimesi", "Changes colour scheme of current room": "Muuttaa tamänhetkisen huoneen väritystä", @@ -185,13 +185,13 @@ "Invite new room members": "Kutsu lisää jäseniä huoneeseen", "Invited": "Kutsuttu", "Invites": "Kutsut", - "Invites user with given id to current room": "Kutsuu annetun käyttäjätunnisteen mukaisen käyttäjän huoneeseen", + "Invites user with given id to current room": "Kutsuu tunnuksen mukaisen käyttäjän huoneeseen", "Sign in with": "Tunnistus", "Join Room": "Liity huoneeseen", "Joins room with given alias": "Liittyy huoneeseen jolla on annettu alias", "Jump to first unread message.": "Hyppää ensimmäiseen lukemattomaan viestiin.", "Kick": "Poista huoneesta", - "Kicks user with given id": "Poistaa käyttäjätunnisteen mukaisen käyttäjän huoneesta", + "Kicks user with given id": "Poistaa tunnuksen mukaisen käyttäjän huoneesta", "Labs": "Laboratorio", "Last seen": "Viimeksi nähty", "Leave room": "Poistu huoneesta", @@ -328,7 +328,7 @@ "Turn Markdown off": "Ota Markdown pois käytöstä", "Turn Markdown on": "Ota Markdown käyttöön", "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s otti osapuolten välisen salauksen käyttöön (algoritmi %(algorithm)s).", - "Username invalid: %(errMessage)s": "Virheellinen käyttäjänimi: %(errMessage)s", + "Username invalid: %(errMessage)s": "Käyttäjänimi ei kelpaa: %(errMessage)s", "Users": "Käyttäjät", "Verification": "Varmennus", "verified": "varmennettu", @@ -502,7 +502,7 @@ "Usage": "Käyttö", "Use compact timeline layout": "Käytä tiivistä aikajanaa", "Use with caution": "Käytä varoen", - "User ID": "Käyttäjätunniste", + "User ID": "Käyttäjätunnus", "User Interface": "Käyttöliittymä", "User name": "Käyttäjänimi", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s asetti aiheeksi \"%(topic)s\".", @@ -520,7 +520,7 @@ "Server unavailable, overloaded, or something else went wrong.": "Palvelin on saavuttamattomissa, ylikuormitettu tai jotain muuta meni vikaan.", "The email address linked to your account must be entered.": "Sinun pitää syöttää tiliisi liitetty sähköpostiosoite.", "The visibility of existing history will be unchanged": "Olemassaolevan viestihistorian näkyvyys ei muutu", - "To get started, please pick a username!": "Valitse käyttäjänimi aloittaaksesi!", + "To get started, please pick a username!": "Aloita valitsemalla käyttäjänimi!", "To use it, just wait for autocomplete results to load and tab through them.": "Käyttääksesi sitä odota vain automaattitäydennyksiä ja selaa niiden läpi.", "To reset your password, enter the email address linked to your account": "Syötä tiliisi liitetty sähköpostiosoite uudelleenalustaaksesi salasanasi", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Aikajanan tietty hetki yritettiin ladata, mutta sinulla ei ole oikeutta nähdä kyseistä viestiä.", @@ -796,7 +796,7 @@ "Please note you are logging into the %(hs)s server, not matrix.org.": "Huomaa että olet kirjautumassa palvelimelle %(hs)s, etkä palvelimelle matrix.org.", "Sign in to get started": "Kirjaudu aloittaksesi", "Upload an avatar:": "Lataa profiilikuva:", - "Deops user with given id": "Poistaa annetun tunnisteen omaavalta käyttäjältä ylläpito-oikeudet", + "Deops user with given id": "Poistaa tunnuksen mukaiselta käyttäjältä ylläpito-oikeudet", "Ignores a user, hiding their messages from you": "Jättää käyttäjän huomioimatta, jotta hänen viestejään ei näytetä sinulle", "Stops ignoring a user, showing their messages going forward": "Lopettaa käyttäjän huomiotta jättämisen, jotta hänen viestinsä näytetään sinulle", "Notify the whole room": "Ilmoita koko huoneelle", @@ -1349,7 +1349,7 @@ "Whether or not you're using the Richtext mode of the Rich Text Editor": "Riippumatta siitä, käytätkö muotoillun tekstin tilaa muotoilueditorissa", "Your User Agent": "Selaintunnisteesi", "The information being sent to us to help make Riot.im better includes:": "Tietoihin, jota lähetetään Riot.im:ään palvelun parantamiseksi, sisältyy:", - "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Niissä kohdissa, missä tämä sivu sisältää yksilöivää tietoa, kuten huoneen, käyttäjän tai ryhmän ID:n, kyseinen tieto poistetaan ennen tiedon lähetystä palvelimelle.", + "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Kohdissa, joissa tämä sivu sisältää yksilöivää tietoa, kuten huoneen, käyttäjän tai ryhmän tunnuksen, kyseinen tieto poistetaan ennen palvelimelle lähettämistä.", "A conference call could not be started because the intgrations server is not available": "Konferenssipuhelua ei voitu aloittaa, koska integraatiopalvelin ei ole käytettävissä", "A call is currently being placed!": "Puhelua ollaan aloittamassa!", "A call is already in progress!": "Puhelu on jo meneillään!", @@ -1558,7 +1558,7 @@ "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Tapahtuman, johon oli vastattu, lataaminen epäonnistui. Se joko ei ole olemassa tai sinulla ei ole oikeutta katsoa sitä.", "The following users may not exist": "Seuraavat käyttäjät eivät välttämättä ole olemassa", "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Alla luetelluille Matrix ID:ille ei löytynyt profiileja. Haluaisitko kutsua ne siitä huolimatta?", - "Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Debug-lokit sisältävät ohjelman käyttödataa, kuten käyttäjätunnuksesi, huoneiden ja ryhmien ID:t tai aliakset, joissa olet vieraillut sekä muiden käyttäjien käyttäjätunnukset. Ne eivät sisällä viestejä.", + "Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Debug-lokit sisältävät sovelluksen käyttödataa, kuten käyttäjänimesi, vierailemiesi huoneiden ja ryhmien tunnukset tai aliakset, sekä muiden käyttäjien käyttäjänimet. Ne eivät sisällä viestejä.", "Before submitting logs, you must create a GitHub issue to describe your problem.": "Ennen lokien lähettämistä sinun täytyy luoda Githubiin issue (kysymys/ongelma), joka sisältää kuvauksen ongelmastasi.", "What GitHub issue are these logs for?": "Mihin Github-issueen nämä lokit liittyvät?", "Notes:": "Huomiot:", @@ -1566,7 +1566,7 @@ "Community IDs cannot be empty.": "Yhteisön ID:t eivät voi olla tyhjänä.", "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "Jotta et menetä keskusteluhistoriaasi, sinun täytyy tallentaa huoneen avaimet ennen kuin kirjaudut ulos. Joudut käyttämään uudempaa Riotin versiota tätä varten", "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "Olet aikaisemmin käyttänyt uudempaa Riotin versiota koneella %(host)s. Jotta voit käyttää tätä versiota osapuolten välisellä salauksella, sinun täytyy kirjautua ulos ja kirjautua takaisin sisään. ", - "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.": "Tämä tekee tilistäsi lopullisesti käyttökelvottoman. Et voi kirjautua sisään, eikä kukaan voi rekisteröidä tunnusta samalla käyttäjä-ID:llä. Tunnuksesi poistuu kaikista huoneista, joihin se on liittynyt, ja tilisi tiedot poistetaan identiteettipalvelimelta. Tämä toimenpide on lopullinen eikä sitä voi kumota.", + "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.": "Tämä tekee tilistäsi lopullisesti käyttökelvottoman. Et voi kirjautua sisään, eikä kukaan voi rekisteröidä samaa käyttäjätunnusta. Tilisi poistuu kaikista huoneista, joihin se on liittynyt, ja tilisi tiedot poistetaan identiteettipalvelimeltasi. Tämä toimenpidettä ei voi kumota.", "Deactivating your account does not by default cause us to forget messages you have sent. If you would like us to forget your messages, please tick the box below.": "Tilisi poistaminen käytöstä ei oletuksena saa meitä unohtamaan lähettämiäsi viestejä. Jos haluaisit meidän unohtavan viestisi, rastita alla oleva ruutu.", "Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.": "Viestien näkyvyys Matrixissa on samantapainen kuin sähköpostissa. Vaikka se, että unohdamme viestisi, tarkoittaa, ettei viestejäsi jaeta enää uusille tai rekisteröitymättömille käyttäjille, käyttäjät, jotka ovat jo saaneet viestisi pystyvät lukemaan jatkossakin omaa kopiotaan viesteistäsi.", "Please forget all messages I have sent when my account is deactivated (Warning: this will cause future users to see an incomplete view of conversations)": "Unohda kaikki viestit, jotka olen lähettänyt, kun tilini on poistettu käytöstä (b>Varoitus: tästä seuraa, että tulevat käyttäjät näkevät epätäydellisen version keskusteluista)", @@ -1591,7 +1591,7 @@ "Room Settings - %(roomName)s": "Huoneen asetukset — %(roomName)s", "Clear Storage and Sign Out": "Tyhjennä varasto ja kirjaudu ulos", "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Selaimen varaston tyhjentäminen saattaa korjata ongelman, mutta kirjaa sinut samalla ulos ja estää sinua lukemasta salattuja keskusteluita.", - "A username can only contain lower case letters, numbers and '=_-./'": "Käyttäjätunnus voi sisältää vain pieniä kirjaimia, numeroita ja merkkejä ”=_-./”", + "A username can only contain lower case letters, numbers and '=_-./'": "Käyttäjänimi voi sisältää vain pieniä kirjaimia, numeroita ja merkkejä ”=_-./”", "COPY": "Kopioi", "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "Estät tällä hetkellä varmentamattomia laitteita; jotta voit lähettää viestejä näihin laitteisiin, sinun täytyy varmentaa ne.", "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Suosittelemme, että käyt varmennusprosessin läpi jokaisella laitteella varmistaaksesi, että ne kuuluvat oikeille omistajilleen, mutta voit lähettää viestin uudelleen varmentamatta, jos niin haluat.", @@ -1616,8 +1616,8 @@ "Code": "Koodi", "Your Modular server": "Modular-palvelimesi", "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.": "Syötä Modular-kotipalvelimesi sijainti. Se voi käyttää omaa verkkotunnustasi tai olla modular.im:n aliverkkotunnus.", - "The username field must not be blank.": "Käyttäjätunnus ei voi olla tyhjä.", - "Username": "Käyttäjätunnus", + "The username field must not be blank.": "Käyttäjänimi ei voi olla tyhjä.", + "Username": "Käyttäjänimi", "Sign in to your Matrix account": "Kirjaudu sisään Matrix-tilillesi", "Sign in to your Matrix account on %(serverName)s": "Kirjaudu sisään Matrix-tilillesi palvelimella %(serverName)s", "Change": "Muuta", @@ -1713,7 +1713,7 @@ "Send debug logs and reload Riot": "Lähetä debug-lokit ja päivitä Riot", "Reload Riot without sending logs": "Päivitä Riot lähettämättä lokeja", "A widget would like to verify your identity": "Sovelma haluaisi vahvistaa identiteettisi", - "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "Sovelma osoitteessa %(widgetUrl)s haluaisi vahvistaa identiteettisi. Jos sallit tämän, sovelma pystyy vahvistamaan käyttäjä-ID:si, mutta ei voi toimia nimelläsi.", + "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "Sovelma osoitteessa %(widgetUrl)s haluaisi todentaa henkilöllisyytesi. Jos sallit tämän, sovelma pystyy todentamaan käyttäjätunnuksesi, muttei voi toimia nimissäsi.", "Remember my selection for this widget": "Muista valintani tälle sovelmalle", "Deny": "Kiellä", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Tunnistimme dataa, joka on lähtöisin vanhasta Riotin versiosta. Tämä aiheuttaa toimintahäiriöitä osapuolten välisessä salauksessa vanhassa versiossa. Viestejä, jotka ovat salattu osapuolten välisellä salauksella, ei välttämättä voida purkaa tällä versiolla. Tämä voi myös aiheuttaa epäonnistumisia viestien välityksessä tämän version kanssa. Jos kohtaat ongelmia, kirjaudu ulos ja takaisin sisään. Säilyttääksesi viestihistoriasi, tallenna salausavaimesi ja tuo ne takaisin kirjauduttuasi takaisin sisälle.", @@ -1849,5 +1849,6 @@ "Name or Matrix ID": "Nimi tai Matrix-tunnus", "Email, name or Matrix ID": "Sähköposti, nimi tai Matrix-tunnus", "Edited at %(date)s": "Muokattu %(date)s", - "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Tiedosto on liian iso ladattavaksi. Tiedostojen kokoraja on %(limit)s mutta tämä tiedosto on %(sizeOfThisFile)s." + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Tiedosto on liian iso ladattavaksi. Tiedostojen kokoraja on %(limit)s mutta tämä tiedosto on %(sizeOfThisFile)s.", + "Unbans user with given ID": "Poistaa porttikiellon tunnuksen mukaiselta käyttäjältä" } From fa3e2f0e52c58f53bc1ec006977a4bd29871799d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 17 May 2019 14:41:55 +0000 Subject: [PATCH 102/273] Translated using Weblate (French) Currently translated at 99.9% (1629 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 9e5b50558e..f2652e3ba9 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1969,5 +1969,6 @@ "Invalid base_url for m.homeserver": "base_url pour m.homeserver non valide", "Homeserver URL does not appear to be a valid Matrix homeserver": "L’URL du serveur d’accueil ne semble pas être un serveur d’accueil Matrix valide", "Invalid base_url for m.identity_server": "base_url pour m.identity_server non valide", - "Identity server URL does not appear to be a valid identity server": "L’URL du serveur d’identité ne semble pas être un serveur d’identité valide" + "Identity server URL does not appear to be a valid identity server": "L’URL du serveur d’identité ne semble pas être un serveur d’identité valide", + "Edited at %(date)s": "Édité à %(date)s" } From 074630acf3180c0e170d0febe4a7c658b3990788 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 17 May 2019 18:10:30 +0000 Subject: [PATCH 103/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1631 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 533ee7a7e8..8d52d4b12a 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1969,5 +1969,6 @@ "Unbans user with given ID": "Visszaengedi a megadott azonosítójú felhasználót", "reacted with %(shortName)s": "ezzel reagált: %(shortName)s", "Edited at %(date)s.": "Szerkesztve ekkor: %(date)s.", - "edited": "szerkesztve" + "edited": "szerkesztve", + "Edited at %(date)s": "Szerkesztve: %(date)s" } From 8bb9544b1285af6bc40222f309f41be94c08f455 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Sat, 18 May 2019 07:33:42 +0000 Subject: [PATCH 104/273] Translated using Weblate (Polish) Currently translated at 70.9% (1157 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pl/ --- src/i18n/strings/pl.json | 58 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index 840595c5f7..70e30c94bb 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -1337,5 +1337,61 @@ "Show read receipts": "Wyświetl potwierdzenia odczytu", "Send typing notifications": "Wyślij powiadomienia o pisaniu", "I don't want my encrypted messages": "Nie chcę moich zaszyfrowanych wiadomości", - "You'll lose access to your encrypted messages": "Utracisz dostęp do zaszyfrowanych wiadomości" + "You'll lose access to your encrypted messages": "Utracisz dostęp do zaszyfrowanych wiadomości", + "Verified!": "Zweryfikowano!", + "Dog": "Pies", + "Cat": "Kot", + "Lion": "Lew", + "Horse": "Koń", + "Unicorn": "Jednorożec", + "Pig": "Świnia", + "Elephant": "Słoń", + "Rabbit": "Królik", + "Panda": "Panda", + "Rooster": "Kogut", + "Penguin": "Pingwin", + "Turtle": "Żółw", + "Fish": "Ryba", + "Octopus": "Ośmiornica", + "Butterfly": "Motyl", + "Flower": "Kwiat", + "Tree": "Drzewo", + "Cactus": "Kaktus", + "Mushroom": "Grzyb", + "Moon": "Księżyc", + "Cloud": "Chmura", + "Fire": "Ogień", + "Banana": "Banan", + "Apple": "Jabłko", + "Strawberry": "Truskawka", + "Corn": "Kukurydza", + "Pizza": "Pizza", + "Cake": "Ciasto", + "Heart": "Serce", + "Robot": "Robot", + "Hat": "Kapelusz", + "Glasses": "Okulary", + "Umbrella": "Parasol", + "Hourglass": "Klepsydra", + "Clock": "Zegar", + "Light bulb": "Żarówka", + "Book": "Książka", + "Pencil": "Ołówek", + "Paperclip": "Spinacz", + "Scissors": "Nożyczki", + "Padlock": "Kłódka", + "Key": "Klucz", + "Telephone": "Telefon", + "Flag": "Flaga", + "Train": "Pociąg", + "Bicycle": "Rower", + "Aeroplane": "Samolot", + "Rocket": "Rakieta", + "Trophy": "Trofeum", + "Guitar": "Gitara", + "Trumpet": "Trąbka", + "Bell": "Dzwonek", + "Anchor": "Kotwica", + "Headphones": "Słuchawki", + "Folder": "Folder" } From 3c5ff566c56386e8fc0b8d88b0130c4fc0dbf7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0?= Date: Fri, 17 May 2019 19:29:24 +0000 Subject: [PATCH 105/273] Translated using Weblate (Slovenian) Currently translated at 0.7% (12 of 1631 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sl/ --- src/i18n/strings/sl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sl.json b/src/i18n/strings/sl.json index 39297c8ae9..01da138193 100644 --- a/src/i18n/strings/sl.json +++ b/src/i18n/strings/sl.json @@ -9,5 +9,6 @@ "Sign In": "Prijava", "powered by Matrix": "poganja Matrix", "Custom Server Options": "Možnosti strežnika po meri", - "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.": "Nastavite lahko tudi strežnik za identiteto po meri, vendar ne boste mogli povabiti uporabnikov prek e-pošte, prav tako pa vas ne bodo mogli povabiti drugi." + "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.": "Nastavite lahko tudi strežnik za identiteto po meri, vendar ne boste mogli povabiti uporabnikov prek e-pošte, prav tako pa vas ne bodo mogli povabiti drugi.", + "Your language of choice": "Vaš jezik po izbiri" } From dc7264126417b4d74068cb9a2004ff1b6ffc010b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 15:23:43 +0100 Subject: [PATCH 106/273] replace emojione with twemoji. completely untested & debugged & unoptimised --- package.json | 3 +- .../Twemoji_Mozilla/TwemojiMozilla.woff2 | Bin 0 -> 398768 bytes res/themes/light/css/_fonts.scss | 34 ++++-- res/themes/light/css/_light.scss | 9 +- scripts/emoji-data-strip.js | 8 ++ src/HtmlUtils.js | 108 +++--------------- src/RichText.js | 40 ------- src/autocomplete/EmojiProvider.js | 19 +-- src/components/structures/RoomStatusBar.js | 4 +- src/components/views/avatars/BaseAvatar.js | 5 +- src/components/views/elements/EmojiText.js | 43 ------- .../views/elements/MemberEventListSummary.js | 6 +- .../views/groups/GroupInviteTile.js | 5 +- .../views/groups/GroupMemberInfo.js | 3 +- src/components/views/groups/GroupRoomInfo.js | 3 +- .../messages/ReactionsRowButtonTooltip.js | 4 +- .../views/messages/SenderProfile.js | 3 +- src/components/views/messages/TextualBody.js | 5 +- src/components/views/messages/TextualEvent.js | 3 +- src/components/views/rooms/Autocomplete.js | 4 +- src/components/views/rooms/EntityTile.js | 11 +- src/components/views/rooms/EventTile.js | 5 +- src/components/views/rooms/MemberInfo.js | 3 +- .../views/rooms/MessageComposerInput.js | 88 ++------------ src/components/views/rooms/ReplyPreview.js | 5 +- src/components/views/rooms/RoomHeader.js | 5 +- src/components/views/rooms/RoomTile.js | 10 +- src/components/views/rooms/WhoIsTypingTile.js | 4 +- .../views/settings/KeyBackupPanel.js | 3 +- .../views/verification/VerificationShowSas.js | 3 +- 30 files changed, 103 insertions(+), 343 deletions(-) create mode 100644 res/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2 delete mode 100644 src/RichText.js delete mode 100644 src/components/views/elements/EmojiText.js diff --git a/package.json b/package.json index 9c55ff43c8..d5cad9dc0d 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "classnames": "^2.1.2", "commonmark": "^0.28.1", "counterpart": "^0.18.0", - "emojione": "2.2.7", + "emojibase-data": "^4.0.0", + "emojibase-regex": "^4.0.0", "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", diff --git a/res/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2 b/res/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..70f9b03c4d172927b0963e27aa9bac98d2d23ad6 GIT binary patch literal 398768 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Ⓢ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⋙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 ShEEqS7GLS#4w0?sO;N|O1Pr<(`$&U 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� z^X0t&|5uu;h$Hcc$gB-y5;Ds!msy*b2)JbiGZpIC4a64D4V~^Fb5u@JOPyYOa^TwXeE4hQEvi$W?Qcg8YfB4yWNC+Noi3S4oiNK}e9h#owk8u2{* zYvN1@jH$>hP&i5$6(9_7;CwfM_f`4(`M;{`381 z_x_63Xm&NL8?+@72V&K#r3g4fMv@wOdFqr_o66BuB9Zp}b$i+OeXO^#$l0BN zo6Z_~vwJ23JCu_DNHN0u$iHT*xCw?n;h^x^MA}bIG*J)Vu8#bAkN~ttCbrPRZ(w-u zR$}Odo##gmbocD&E;EcYG9gmgN;)^0idVyG zh3wzgQ(T7B$vRAt_4|!tFeZWa_QGY$ojRlQ#SGLNMB9zN{jXbPOx2o0)r;=F=p7@AZiL|pQm%8*mP0_ptJ!5CzHB_0f)2BGh)Y@Nn}JC z{bJ6T9$fB8LlrfduHoE;3lj7(f6!8RndF`pFr=E6^bf9*Hvg;yY|YJ~Qi9X>jbjuO z0(B){&-@VviA>jXr~Dzm^rb3OE8-lyEeSo$mSKeyPo)h5fg*kp8lINI4Y-Xmy`v@N16;9XTRS&!$T2req<^TcMPRGu(f->1sga~9;TA@6 z!T$Z;VUuMV&W^Yg-QJ-&*V+=&CJgH+gS&#$> zT9#)A1zw0U{zYM>9Nc{rztbAiP&m$>22xFgw8vTA zuWETFw;-Y{1{&jktju6T0|s$`E$|MzZk~-fO2ZTt3!Zdr>`w4MDxBD8XE!hJkh&p; zTdn+IQqNyYCOLh3DJ=)>Z@c!hPX=voSv&gy+I5kIrKN?Wq|e$1{I2Vr?32Nx6I#bi zXnK5CZ3!c0|3wV*`^U%UFCV`YmSeCN%kbEFUb1$NcA3}y1o+ClPnsl6S|X&9HS)Cc zw-b>`g<@3_Puos4uPVC`ai{eIMHorygT7{mNu#X|cw5Pf`r+e%(*g=@1xR||PsuuD z6owK0A3Ye%@sjfoKKpz9yq|c0>tX12kCYyj9tDT~2nJ$za>#o$7DF8C4@(!MjIKOj zP2S}+SGxz=I!43&6G2r=rIGKczs)vv3X|lj2^KL&H*Lc2u;6yX_}rhH zXLt*Y{m1<(1_Eg`6oi*!B0qn`3L`>64a}zRl81u$&sXMyPxiT>&o}d|CCjN95V@o# zXafp$SDRIh4wBGec(ld=S~LoU8?ZivP*o4iGys*=cJzW_E8w(NnbqBm+;o7NNoh=- zzbZI|3+68iPSuv4xbinYyFq!vbv=kTgE|Y57+58%cXEceeC6?~y}1aS1PPsJ)pKti z1^~lWIjVxnZw8reEOjnj;#_jW6nLX7XqHG~Ec~mluc*pJM%Fuo&qR%49!=CR_VRlAR_mFK$7qS`6;Gy7|GZG8Sc* zk}?1me|O~<{eT_g^KUAaqBuBaNg@xK6;;EV`=JyDXHGG#F~rT}s?Nr^nIi=VgI;mf zvz^KMp*SuU3e#`{2`sPTYt=x2UC+ZW;rrxU-?Vk;Yto5ozZ`#7sU+6)M>oXd9(p|e zzD$des3(nuUQcxcCdL=u5<~0sSHeQFov45_>ogjt#ft5x#IqL=`!^y9BqclxqYs{c zO4HimsLmTBg2a!}WcgLEL|WPLCVkEtVvNtcDozU;H6uVud`ARn=$}qJduOF(f6F%r z>+x7XxpeFew~8Q%Gr#5GY$zJ>jfo=d?C0A0o1RXw$G`mS@r}LVx!?3Qv4c1ZVQ^Pb zD!q#|@Xv}n#pHBSu$C#1WDoaLRAN+}wvy``bV5OM@=c)dDDBzdEw`+uhUF`OaKXL; z$rHkNMsNtH@1dFE?vWd}(+#S+&CQgC0}i4ONA>!>kKTG$Q{pYn&1r_b6b>DV&DUI4 z;pd*Wif|jt!EIZ0$-}Da5~)AXN>wo~smRlCu9Tr^wnM0Q4Zt3Aqdb6Ab`AK8c7^`hLY9d6^RtIN( zh^6T#mIZHp8AI}P?_uia_8=@>xwf=i63=@TXX)SN+OI=UaUaaWmraKzlJIeG5|IZM z_@BQSA-Je~AXN(H1S$P5;|ErPVZpTRuP_;55tvFc`Ls9=*z>l_ZxAO)-b)63VR3{v z-NI8q(x_Oa*$wZ$4t+l~Qy4I6pj78o3cT%)y!PnuI$A+?qX25b`G*3nU~{I+R=czzE^9YhI8aBROH*~M z;@;F4ks5iiG<1FWv%fiM960uA;U48T1EDcXd-;36QeiW7nWo9K)gPwB3B&dFYpi(> zVwUC9G1fInYt|GoKW$+{?%R@5G|z%$)gV!0BoYS)fr^ND)4Y&qy`sA!A+DPS0_{th z5!>7o)k8Ao`jHtPNu;wD8W1XH_$cU>m2+t%%>hDI<=d+vfQ-d7n;)t$>oYc=qW;fc zEwFPZpl=)JU-IAE>&6I_*Op{SC}z^U<-c zXwqfhvhbAXD1jfTR6daJo;HVWzW;ui_y7KI8C$Xj{uEt1AJ*(Wgx1y#d#^fAJ;T2~ z0r%|iZoK2U<3All>h=j7%oP&mva26_>IPph>AZz(P>fxSr0Z;m*pS%5aEQsICA!&R z`Y}ER6B6MRW2vx%bUuh{>m1kQt^*UzO&9JqnD+cuY)h0MDUU$g;y0%K8U7;@7yaye z^J@zL*3i_7Pg8#_E`rzVu;Nz~OP{Z(K2@pVDK%@Qdb;3D8Isl=I2zHC0^N|Gcos&2 z3-~0X{}zBr=JQL}u%>v{wGj2rYf5lBy&9^ogX^4sa|W-bJ=Yl+?B>NG_EG#%&?1=5 zrM!HrbtpB8rUaim=Ogl^RQF&&OACQukK`HDs;^8O6Qzk))LLM982NfksKZcml`?Ir zg6u!Wl8?RH{1cMJ(+ z3yuI>;Q(YQHi01GddxBUJ%g%rV(8w$_2-2WYN4jip<)&YgaOztSt0oTMC|>bWk*tk zUN9dAEeJZbX;h$NI12k1)3cAlT+XAMYS~F5NR! z^*re7W7Wj=B6DeDTqSK7t680tSF^WKa0Xbjgak1F)PhWqh|J#5x-i6=A?c;YaLxOsuHDg?be1Gy9>r@pJHDzr}U` z@9=C>PSpw3bqTE&|F1~ol_!9~_e`nHt7Ci!ig&iA#o*Uvx%1#4F< zDP46#bPfDFBlc9Re+i2rq4#F^pn;ZwLtxi`=^Ey-kZNgq2BFQk1s&OQbH=C>@ z9U}ybGD7d~F^Nc1;j37&tb84u@9)Vbd_MTFX24~!Hor;)U$-L&8ucS>;h92#x35#x zKMB0`EboVJ&-bLB?jGh)@mx#8L&51BzCD(2u5KHzGC9DC3scppzxD-us;|sP~$sFl~;l>AF7MVN^zYVBzrzb27URX**6D=Y74elaDsOZ9|h>qqp~ASbk}Lp_}y z(60KGdc#Y;iV8muCbHcF-1icMf0=YN+bWQ|%ty#5N1%P`xf8HBV#%BfgQQdkG6c8Y zW2kA~s_pv7^P1L`(>E@eZ$?d-em;Mj-?yiD%!2J5*IKOTc?e)nD%Q=R%Cc(0{Vq?*7CxNpBX6!zeQT)9q6mY`}?s0{{;E$c#u=Fg%GCIbg3`JR1X5tO48OyZMLPA-RLny6wi+}1W-~1aE%8EpqK0cu!9NG z{fvB`dX>nHDn{Whr;bQRT)vs>+Z+w#g?p#q^+d zKO{h=k4VNfjGP2!NQzSw^tuFF01CMf7hny?@+%wTf1v zVKG9`$?{FhfOOH`QyJeDv+jqEov#zZ(f#x%$bh*=vR&@VPZYj$E2pfeDF5Fh$6lE^ zKfT}#9~2z*uUzY|_*n9T3~tcMzpGv_C0{-7iveYmuh?)EZM^M8cWl#~2-1=^E$rA3 zgv-9x5SjA&s`g+gg%N@So43t+;x4Vp8;TWtA=Qy-08&Mdg9@+0j^m0#z~V|I2X|aa z0H%jYA&_=albmhLdo75e#JSutR76*LZC&PsW*xK^42DnG6!+YMhMdL%ECauL>GXSc zT1N$*c|1M|MO&&Bwq0!%k>vy2KaoO1*pK@$4Sq+Qzmhi%AlEok9{}FY2BOZMOB)Wu zwt>3&6+`b2mH|m>7AD`BRDv~g)1mS6pCv_ zkOK8x5<4UVix+`gz+?x?T@fLXS=t&OPX^6_ZnzdB!!R<0F`ppkAuTV#2L^|u-2-OO z6V?ngIme6N)v3&&1C@fJf_mYG+6SsjFBD7i;$Sgoz74fhN)hg<@mC}5AK_VdY z!Gl14qSHgvuJN!RANS3PP>2JhUq>wuK~Clz-wE`^bz5sE>Txc~LwgBSLlmH3=IPRH zdJs({HO}~%60-6F8@g)JSat23%R_!fGul=>fhbJ7k=L+<$lxq><{uhVV3nwX!&}qLd{!23PP?}}@%+Z~iREC$tX|jW zF1^d6v(j2-a{V=ZeKq~8=TW}Bzi*DMhb<{SWzyoWVNcEMKUv(*Ei@Jnz)ZL_V-aBu zQ-%^A&a84(yYO~~P0Cur#`&yD1XO6I7NT zP7T=F>;sHE2>`8eyR#`u6BSQ1_%}54Dvkfas=#o)^w;*Kn1=X}=LqG>)|HHU}RmEb83cXT7}pRjpfY@UKZ$R2xV_xEu35`1^*=n@mSm;RL}(jTh->#XRf( zYGV5g*bBncfaE!GJhV_XE{x1>ohye!+p&`sD{6Zh}#{x zLN#DcY=;ESczhC69{fYa$G7?A!B=S6q43EyZ-dMH+Z_p+fZU=IsuZ3NuU4#vrxu=? zk_kNJd=Q-pmxxQD7EGZGToiZk;5fb%BQQ|x%~I?|)36@LaIs*%hB#2IPrPcu=|S3K z4X06378`YkzeC;Tp~BIU(Rxb#4NIsA7FS8~5e%7W2cV`F0&Trs=cxk)o#}b(ml;rZ z*PD^cKcvZY4PgyBOWFsy9JpUUuTY#v4d>4ru0So>8^?g0aqLDj4==5=+ieaJ+-)p-$jpo zeDT{wDEWr&#%@nQVUNm#sP98Gec?fb;n-$N3Jpx><(|&AnCDze$&&N3lLx-PlI!cM zR)U7t21X*@vkv}$0q#Da{3EWdugdwS1ExIyN?C<8a2R+fN75t#k^Cdas)|$mt^>U) zXKLdQG#tkmS;z{h!feZ$_8=MSm3-tQ>_ubfFVi3MG{7Uqc{q8iF2H!Se8gvrK+j7Bx{q@uRKb&5`Sy+c}q9CGR z_#=C;cOzjM1tZ==C?flcq=eXXArkwKV%k^spgsQ>4pZ~XlE@}4R)_l}8CB``wERDM zo{(Mtb#pMTu?A-=fC+m*oQKboN#=Ajw9oN7{vnO(t>OBa!8^|I)pz0_ z$8D+}2kc$ZvGnr+Pbv+g6dOYAm5vx0z!x=;M4d*#h+>lf(xQG(0Ef<%IG_9tfVcjZ ztwY5r%Ma}Q*Gw{d6U3lGxaK8?Z%}|7ctwKSz--SX1Q(hV$zQ}$l=M-|!DfhP-fcMa zIADy&q9^^H#26CgShAT2{1)Phr#f|+arGWe1-h1F${d_K zZ~5QvjJ(|js_)he1cMs^K0)ea^`n3h+dmfT14+3R4Y}}NRqBNWNH%9~-1%76R8(_} z*f`V(x!WzjQ<*m&cCm|?mlRz;)O2L-e0STC_Gn;)uBCKdH*vl-9XOUGi5~RLwgHf# z;+W=lE>#=ETiyyJ9=)@^VQHYU@`9zQ^Y1vadFcgHq|tt%l;z7~tU?%zJ0~6rbjPpS zg7Tw(|NHz-lTT(qk=<2lfGyj44s8HDdKpAIAq#g6P9RjAd2=H-*{Wl;DFxc$FTz-| z>a#DeIE?qdOh|T^0~*dWwFXJ~Wq@qN5H+y0z7slAmX>8Oy9pk1I}AZ37@joa&txac*1ra~9)WOXUF8&;s@3Znob6$Nm`y`pDxBjkm5N+p zIsF#Eb3pSyV+DIMf4Z>2@9WYd8X`3L@eEOKnG^;4J!Kuu)>nn>Q~JYa-u&mu&n}DK zzyCEiMUaAYS#*hR$NC-x4D7AYpXDoRLL}x~$gmdAdqEds4z&v(96H8#6tO|A_#uK` zt=6z|VCpw2Qcllp| zS5V5$z1ecvTZjIP$l%e*FBPwfpGY>1-3GlD`?lfMb{%B5_2 zc^L4A73v0|yn}TLY2Ppmpyzi)F?mAfS~i=E*Y(b;pypUFW`l^K_!Np!UUcMAb#QIUwa_ zcii!0n01Mv9c0B%AYNlW?A@$J)sj=5#3w5&$0uP7$?D=Q2?=2mX^UB0jEKQYqL#I_ z?;k`Zdq+neyPJ<--w>or36O*)08rbp8xL>3ckfAfQuua9?f*KW=|DCDVir+@v$D~2 z0`Y2Hv;{IYBg%_$F#!t>=9eAK6_aSoyck@M+G zEK9yx9x2d83{jA+q~?U7mRFc{2TbN+OWQrZd#*W1B#~(#ONiAiO1JH(iuYm;=nT+h zULFg)HA%7j=c<765feG<{)s!-1rbd|FxIJ0eS3IY_AmZ?R0KJI^bO`ZR{HYh7eR7h2ENlk(>%de6?1LYU6FNjyo>S?yOWMq&6RA&UKfFCD3Oo zIdj^}J{>gfHuBb;KH-DTcu^r>^*|v6-0_l~gvGzN^>fFYS`e9*7`OtmzsnU7OJ)_q zZaDu#PhWvj2vQ9ntl+9qgN>UQrhcd|O}2M902+$_)z@h2Vf_`25#mN@ogBZqge=yq z@7G(P>pY8Sc&g0almlRjkYUBn>9jgX&b+QV8idkZX(g1j0pv1z&dk0OKpD!^22o< zz^cO1$+P+`^TmhJ(7a&uJ$RUM>fX?9v}H8JKMTShT0dVsV&6CZro1n_8R*s3mMk?B z7sbSX{F7x6zHE@~%3Ko-SYhisw z6g^CaIQuTLWtRKD*qfm;*c+WlkEZ~+9|It2mMP~cm@y(4bTg0v|861f<1ttTR|$G4 zcK5oLAaIf#pDo%;6a?1QHe-qif$eSuc^yNSh$2OkaDGK~oCpYIA!qfM>JuuL{pZ}G6PYjjxgK#LCLH#In)xMz3H%YP`vs+hm7XCZJsK#jPn z>{6`vMPMCTd!jMg*wl0oZj7snNSwNq8wSM* zo`y2c&SG&1Q}YTnab|*q7s;F=hU}%ezAx&5NRN5loRV9LXl_*+U&yImL(15kW8E=+ zzqxiJ;==6@l?C!vs|6-Hi)@($IF1QHflGN@N-|-!1`*vj(cM=sE3#&b$0bi#6n3S$ zB`Z4foxHhM8C5AxiqDGkTtn~;PAn$LSPdr0X4W8as??4Hwn%M}jwWof-Tx&86h{r)xB9QUo-Q0NGuA?<;*1agwR_HB1D-Wl_JG_4{&6N?oEjJ3>I`Tv2&n_*N`-4kK`<5@keh~O`^SyIM#RNuVQJ2Q zSS+|81OV(?7I|XwvjN>qfe6?n9P-2uSRQ1>dMndoyki_FsjQd;vVLVTXI01M)3;mJ zRyVQ0STS-ta2Wk#2d$+U-1FDxlBzhs+YiQ#3(L;`pfy?s1Kl zR&|5^$31+_z^u&+1P)$M0$D7%=2A{zI(pOueBJzWpB;=Ahq$4%A1&#PKLUKOqg3zHE0W%mS&w7Qfv@D}rA~W%Ep3q@&N#7df8gv!j6e zZ;7f5V>wOa&|tw4#H>n8qI#5BJ$obVBkS|TrvU^ZJC-QN_-FbSp(i36RFa87C0qFq(%JmU3aNNc`z4sstClSpiIj(8wi2T--#pg7MI(gaFy<($HtE43q*= zh^gO${q7tLWgyjS7|3NxEP{&$ITOST3W6d57GeSlu#^NdCCOz`B_JA&D*$L^wq+R$ z1}a2CqC4QEYMIm0NN5EsGokb*ZLFuQHI@L>G=S7hksA@i-O0TBQ|2AcXSjQbJ3o%S z%ehIFZBS<9$Nq>=2fiQ6-Bq|0Ze_08e!5HUYqJMsN#iPUB(3c$gxeRCQMCIw#zf=l=hbcN@8wAQkKstvTSeSx#9YEb7r2rBifDQdTwaT1zu*woW{*5%_y+6>X@uxXsMlbb5_J;tInS7R{5--?}LCHzq{qtYAs||e9rN8(%KdXJX>DDr&QEF za57!H>}1cYWBw_;()ZB$3ST1aU32}PE6k8q{c7j!@fIHkypSUz>k`w;UaQzlw4G?P z|MHXSbe3@#0qPJ}z|tz+>=UG5kR*dZzeL0Dcczn6m*!d3nu0nF2|tPKBsB}BX|#WU znH&3jcI9Q*yL79P%HXGi?p&Z`rDKlQ)c4TF&Dvf@SHxpgAjL5HG#U}wtHc%O!^Tf8`AqEy^k5)1?Z#$$BeaW8PB)mpU4_sw5& zllw&8uP5hXe81hH&L`aNCptq&?kj)i9ypv)X2ay~B{m?|!(qJin4?3!we}8%^t3$Z zw;en?Kayfyrv6s{?DOQKfvnuC?ipBD=U--uiLD}1c5&R6x*$QM2V`Bx+N+;uSDN(q zO!XZlUmW=S>e`kS65@RVbz9J6m(?uCs!RldztovC*UVw-XPPOqKC_0+*UliC>W8}# z9BlmqU091+?<2J```R?8Cl5CG`jRTx`#lPV)Wvr zXQnpsG|5q2yqRJ{K3{JXvk6}2L852&kG!q8a{LlD&?h_9| zPKy{VnLXD96&veOk)&y3jN~Lvk+?qE6IbJ&F=j_mfN04YBOVvFLns`oaw1TCJPbJ; zUn?+?aixE2v@P1BFvb?si7S%K0%Ax=V$>)B)+I=6iMDrPy3zKS5KM6*RUpK>d<6vR zx>X=VR~RGaISY<)gB`VZg(7r{NsI>m<|-5+Iey@l&4}q>N^x23D}>#IVz}7dwVIy& zmXdU@AcR5{E);ZQm=i53eQ(F_)R2$lWo#oWR^c zLJ&?7c(j_JNJ8z26BKTJw=5eGSppz&+^S;^#)y*?Zj?mJzxIdV0E-yOX`2%G8K%O5 zXe1ZMOClB0AG&d&zylM4s~BF2M0r2~`}6$8Sdc`FlsKysU~z1YfAT~>2eD#y%Ju*? zz7SW(#f2`rP>hPrt`KFq)aYOhV6sf4)g>uT3H>#L#)-q3pPBC zhapP>0$WNy%|^slCnEBzF;5nV+;;_`;@A=t;=lb>i3^S0=;kjVAkOJODGq@gfiOt( z?A!n}olp@$?sl9cak~a5Xgy5>{p^dYnVhC&*K~uy^y&=iq7n9l6`<3DntfgZZY>! zGA37r<)PlC5hZznXdadqzfk3kmZ3!vql(~c7bvhM9%Nj`c~cUq&gEL3;Y}-NGwmCJ6oq!5)_3?aeO|3gZxRYRu>_~X$F^?&ocK}U-mvz!MtL8 zi65Q)C&0$$d37gaaV>sBG*6(c9*RHaR!+3w2~Ff->X}sjmVWJy)CHp=-+72gQhSQpZ_xSb4A-^~U|h|7 z2lq4MiiDL7U~8~}vM|Jz`I&Q0SyGcp;4T<(io{%SvUeelYcRZ>z%)1k)_i$=%^JrC z`S>2w+Ipg|F;Nkfl@VaoQf2UWKlzJ~fuiD1oeo08rV>OXJox7zp(R41#ghK-91xV? z7?$NMUKG%~U&--H?zjSXPog?xwBpplDbsKRdevd}j1?tml zoLYU8LF?2QgwJ7r=T%;eHf{xzE&#|s{VJFganLeh zhvCS(e54*hygIjw5DDy<2sqi~bmCpa0sen(1bK57S;8^g2<{{2Q%T~Za59Z>Mvwr1 zSJ;w$M;2>6tWmR5+-vWA+YeGCCWba5OB=>j=w7l3E%n$1AW4fGO*lZkV(*vFKR#rH9IOu(@FJT+ z(AxsV<@@JVs>_^{XYadvF}D>V(p^y~o=^ganz8!L zeI}Z&F>t*AsS%4jL_DS&{VMN6Jz21j;r`+dL+;ra#i!i`s664yF1ks2b4!7!<^94H zkt-Iy?-o^Wxycspy5d^fxqiG%_DGsB*S~6YqrOEF0v??PTh`q#j;Ia~dzXRikbUNOFTHF09ft zfr0!M4)X$kF?)otGoS^cFM-l!6gGPBL5~Qb-8s<$wE)L+ELYf$Qh&qqF-ty1D;|p7 zlw(X=#3LF_uFwTcRN$J606jp$zg$+IypM$~+I@qf^yi?aAp6hjEqa%r6WSrfRm9_4IN=fw?eYdVNXYDx45D?NXG`@Dti-<5S?&w5De_lSn zpM#O81q=J#%gTQ&Ksq}Y&xww@5Ugx>=5O?HrT9XVFW3& z+hu&|99hBanvD|4#_*1gaBPq@vkPQ%q!Oph;V?TKGM;8Or)FjTKCxt@nHjNRFP7yx zb~STqII}gtX=$q^f@;7m(z95614TTp!Cx<{*=Z+)BGh0AobwwF)Deq|R6rv0P4eMa zQ|;@4e!axKy!<|Oka5BMHP39%&RTmxNrokB&Dq<}td@SE>D99NWDGlyQ8ar;1+{R& zY`60A`eKLS{n|LgaHC-56&81_oAP5>8EJcSU8rx=XIOub!vTGw zwt#~l-*o&AH#jke^eTGgyp{9`ifG}DAxULbXt9urkz@IIM3sV)VNd%597aImr+_g& z27}L-&`(vV2WQMUm2!VF=_VMC zn}&^#35be%Zi%!!hszmo zE&8%KDg4T$_?ug*g>msw)mtJmqs}dSU#B}4)%_mE(C1O)1zl3!JpngpItk^M!*8+8 z$8W~FOEGv;p|!>ticnu$NwRtES$doXbyLy#)BVkyAW@*>SJl~YtZUuF(|bs(e_gi@ z?b^Rocd}@v(qNUAA%Moz^TE>0XYecOAvK{7n$)w4uvz{3c_iD33(Kh_4Q8?i%f^sF1>4Z}P z+L;zSg6<>e+8@4>c>Xfbti-5y0)R{zKqS1Bf1@?dDzZ2pAR^$RHOh0GNBrgle-cF= zbIy%+;0TkmR*e;M5FDK@0LupoJNXm52stzP9|Yh5EMR^Wb+e*du}pDZZ_&$yDU7dU zU1l9&jk4C@`1_J8B^8OD9*l&+OG!V;79%n~HEALWSN7t#4>&v-PSkQ9{?%UCT`X;7 zkmJ^qRjm?$MH`6xIbxD0vJ@5H;49IvlX9!8a%_eVD; z@S}alJ7}L?1x`Chw-xVM>)lZv86K^4eERfIr9){Ix?){rx0sd2ymHF!An}%|h{^+P z&-|-6X3wVgJf2_qI7GK#Z6OILLqr{vs81len5KhN;UqD-+Nl*Y5XAmQ{eqD|Cu?z0 z=;K9@JnlVl3LTNBTpq&x>n3!uX|M?+kvs7cbvbJxPNaQ?i?YjZJ3#-;k?-AyfBILo zE^gX=#U0}9D_?&?0tp>!EFN236Fvi_FP2TWTk#iJVX2-(!b@{$QZPsoSb$|+#9j}^ zwu)cpWJw;k{LS5?oE(N9ah5Lti~UUQAF0Z{sfo+3?om%2T3!hVw%hNA{#`x$XC91| zJna1vppt(&u%s|()7R#JtR<4e&Dr!mmR_7i z&RUFR9eQk#tLN(}!X;&=E^BfUPwE%9;BjMutI986A~Q@>pqb5z%?xy_VsEKhe25Kv z8CS-Saz)xa2`R|e^GD~v1u?{Sy17diRn8h^j#B;$0@)9V%)XI$?!KgZIx)xwDS@Yg z9KP4pMq<`s8`?dF+`#(tXAv7NF@aX5o7kIe3)67DFMcya7WuhoW|>)0lc>lFzPz-= z;CAuwRzjb=4qt>Ma8v~p@dk&x;nebC4O=rg(Tvu<-8L=}ttcx26|GhrL zf}`RT3s~%2??@a+5rfeNtPVYV;j%3mk9Va}#DkM&=(>AVcGulAoBp^p>)ErvFw|n$ zRI3Um2~D)YH+f*7t>E{$42eq2Xs|m&?fMrDWtaq4Aze9~d)_{HVHZWqXBsx0*!67T z7hm;r`#m@^McgH3%GH|j5$x*T^3`futq(6FDXxl1zL-rX8OG-XE|poL%ljTG+cK z{(MLDmWLoSXiI7(bStpuK{YkB$4$SL%~Vx2gS2tm7FXFP^lylt21%Qf#c*Nhp9)LH zx5LgVZB@$Ju+%TQ<^;nQHKXL0HUMz>?6I}G<;jco_QKrktT)9Rg{|H@0^f5EtSI(Q z-da0xd|3zAwQ8%+S}Yt-)pe}oybs*r?eO4IOGtS`La#VGDylbtkaKr1_sJiEgSTZ* zRB^Z-Xwv_(-(`~Pl6q1Jq*YO?2mTNIXypcAgUa<{*Qtlvt|M*B2+5@`+ja5XrN7;Z{q=;4blaA1*bDuooi7eUt7RL#DMZ z#)-iGoW*DsVcfYtP<2KL)o}PPx>lA82xA|JT>!wp>zw&bAft?HTlOGZ!=wya zPf;zr*W8+5#el|8pGb}m0>-*}_wf^V6_vWWK*jtyRybK7j?(E22f&ps%d2NM{xhtu ziMhfnUce-VdhCcT3;pcS&__{OaS!==dC_?|{m?+oINwt7==?)2yZFAjSK7+;9xa5E z0B2AMewUv+2dU)*-nVrLA4;pqMBy6^OKbx0(7DXHN)X`PIA24vbAOi#BWDB`VOj>p z)wSQ*e6WwQC%r-NMHpuAc(S^WMVNN@3)bFTOrYV!?9?w+=9r1P>cPS4d{bmHYIdPm z>pg^fiR+6Gmym(5%dqHCayJ!)j&Dpp?(~XBa`~;-s;vOfjQV)mQDTL`1_hBl0id(~)9ryEPe=nVO&YHgqu)tIS^#Q-Mx$F0F|waZfKNPx_&N z7t~<~Hyy3UAC);bKaE%h=#%@SrF5;i)W4J3aWE@lV1ourS{ex{nJ5w`ItARuj)_^( zZjG!)x9F^i9gSRpQ-dRkq7=e(3z|;cFc1+wHHcDLO--8Gp;9^2F1>slarjUv|K#xA zS1(3m^^GIE0J9&RwAAnOc5G-P^@NhxyBiAevQz9_N>`Ryvb-QW1GZkQSYmM_j~qz8 zm+f=g$13X1KcwVQT-L^l1pTn>X&&S0ACCUkshlYRnYDjZ_oMW$bCyHSmR;e$+}yRp zy{DyD7`W@^FX6jZ_qO!79~t5h)C4W-Iq&()#o-?W*>wuc_(EYOH_;dhf?UyL4%Y?PeLkIH9*@xbBzYQ}o8g{C`?cLh%CYUq z$D^q^=A^l6&A2N2pH5W!qX3#e&S&xetLeS(3=()QGwek#+!S3gMm!Va^79+EL7G{8 z8cL#TdF#Vg=Si;8M}71A9%g|kP_+01zl|o%uZO_Jbc&v)Y=>)0D2aWB*5$58K(&I; z<$4IfJE{)|2?2`$ML@IvqS!aGe9?p%&3&bQh+|&%qdWhOd=Y*x_WTIvtftKk_{~15 zwd+W%pUuq1s5lOTfob6|Id(^6jHFrHXNqz2VKOz+GHahE5@+DZ0rZGRY~3BFjeVRj zlZM%>WpYfRaA3A~XlkhZoBcz`M?StKjikH6W!%pm>n2DkKMf*-X8)^th{I;I@n^Sp zcszeTy5}d46%}P#@HfSdoY93QN@_CmHP3F;fN!KA)}iUfH~I7c><58uY3FH^CXs+6 zWdK5LnzVh6SV1I{33SaE-O^vfw}Q3tdH970ju+01>=hE%#4Q4x4C+F>(ro4;__R&JieBXeF>5E$5$xc8 zYfLS8Ql1*=BD|_Eb|+??NPn3aeiCX2pX>WUn^iRsG(w+zFGf(eE`8;72`h zTw?DMy)MKeS{CCH5!dBX!W`~g;ds8-^Y`VSfiN}_$!+=lNl*&B(M08+HYFp1UCK!6 zwvWq>Vd6wGOtd1S-@8hMU!soU_OvJAoN*(E)Y?|BvDGl0CthO0ce>Pf;H|aq4DhdB z99pAS?6lXEtadXc&~B?OwG$+kHwI9Hz1Fs7^*&f_Q8M@%ZAffAoeuuxln{4{Dg^aYQ!{w+c#xZg(eX^MRvnlCE7=F$1A4B8Ed>V=d8_QHbJ2{3F?-g_6G7we=};g5QPUgjMCX9=xS^uPtlLK| zMY`dIr%{?2TIHD*TSE!`UR?p2B}0PYE>_*t#>SQ~vI620P7fc+-Lo5dF5$_d!rSBUX--q{= z{05Z+V#M6F=M&WVu{GMyf^qbUWH*wN=WQAn6k;xxC4`RDTd0Nj>SIx5P<{w>E6)u zp|q{xprn*`Fjo1UDt%or+M$LCR$KMm}7=C zoiS~+OxGi)uTps#oj$h0bRBp%Uvg~ za~ELl4+b*h$8V<=c7D3Hxrre5R4tPxrNow@C(NRhe%#dw*fpnH<~f(fO@B^f^d1Ox z703rPE{3|uKKAApO?+QNP8l?~vzej0$mk8Xf_FBY$Xmr|6@dAZ{28Z{Q74kUp;uB` zbb~yYM_5P570@cZsMwttteO(hM+Q-{nS)OL$!k7YE{LAU8f7Ji<6|}xwMl_i@Nlfg zB~`+Fm0FjpVg`fh+E-yBC%UAVD!&oHU(dG+zByVe2uYQqor-X)B2l8%P?ks&8eP`V zpeU+>d6|_ZL4Q&{RV8(@Mp@3$sbVMlE-dw)^RRKNauOG06^PFldYW)a(F7H_uNMpi z%VErKt^)4?jcYt-;rhhqu27#$cA-W97xHd&esVeMGAqkf2%v{Dlu3iFhE*Kep+{4x zXiuOtQWc8IE0bCsnhx$ebMVCjr`K(x2z;=;AZEsq3-q_Qxr15V4<6in^Hb;&M5mrM z?Oh84_x$J#&7KmtSGjT1qC+Or@Qi(%j*A%-t0sigw+w!+ICyZ@o{bw9P*}+9o;FS5 zyLjS2rL)GBp$9vROybBDE5{S<%quXmY}k%}etRt9KAYUjkJ@$bHQZ3u z{y)v_O6Dm5m<%iu5{F8vc%e+T_^&bm@Vnc9=-3{8NU^xASga4};YPX_`J+tEO#CKMtdAOv?{Bd$=rK|a5@y>7Cb=LAMIWuApfC6@q&uuHMR zLYXI5drokhT-RPKpU>KW8xex*z*&8NfV4biYkvepe?mMXk*52l5??=^sm-4@Xy-;s zD&-oY5Y#EyZ`T}Qsf@m=856sbFl$BypcmPJKmiQ_)cF)cC!&gE5~-jRBrj*860r;g zS7LrF*!!Uuvq$UZ>=o}V_>nv`J#v>T^+KThX_jWH(Vsl%Q2MO^M)>0W&< z>}>hvpTIk$n&Boe+7bWyOm9CfJ+8OIf(wHeqQiWD+|!{I<2Fy%=)B#%H9A=EZprzW zaQ@r9ohnxC9%_NMwGH}i8f;E`x`2$^1};9vJGK~Tjctj~CJ%@1FWewIQ82mmFY+w= zg0)KS8j9x_eicLIb#CmC{kAXVkm$la@1W5{x4GJ z7{k;Bzy>aaAz0}fII`MW!*?(pOukxq_0B)^r2<9>SkeWT{+UZU!^um$E&=c*l&+g~ z()!ffaI(qvF-2||b1x0$4J3E7@}xW<&!oAsvqoE<}RiY zJ8~4amG&JL1c4o28PzX6jI?V;PJRJCaD?JX$ z1sae8bQ1z~W`9M&UC+CU0_ZK^V-4~=_kbdER8)yMz+IN-XTZ~G51ZuFOjQz7|2}kk$JdB8Oq`>BCj5UC+goU3S#zi&fjOy}QP7nKM{G zh#Cr&Mg{s|0NVZE92 z`;Bqh7|oRbxd;JnqkpD~W0vN&ap3nAw3CAvkV;2NK^Egjrnz#%w7%48-vT6X84b^7 zW;Jgv+frmM&u-dMwzMbEL`~=#D<4b6|gdxe@Y3vddI5#wqqm|u`oT8!}d$&qKZfqbSh_{JaFGe#Z z7?&`VWnmJk84+AVJ1I)F*pOqj<`@?%eMoH$M1)$%;p@=A-?RUwG;ElZ-L0+c%%RxP5l4 z=+m9ff{eH^ATa91vm3Cd$k9)h4*NCGAnW?1gun@|+o#+T#eNBi`l5^XSZ&^{?KJd0`G zQ0TSLMU0k0St)V3+TxcIkysEp`HRgqaeh$d5~Kf}H5gW(Er}5DQ7|ECw4#7vO+(Q8 zi}!|gczo-tmq??@h?0z61XDY!K{Z7MvkWOaS8>>w$5Ci*yhd%Lm0Ke$23AiulROS* zd3OX+A9<*L)@J>lV=UItETL<|X?zW~)he^2+;?^Zk2F0yt3r?Msr8~xj&Kym-dVUz zi-y2s_;>qlRGfIlF)<8}P*c>R-uOS#pNCBs$9KvenjN74-q$~Ws2TS7-8&Z>p)<^9yWekn?bG!5hlqH?Uq1*^o7cufpRwJsR9!h#gaA+p8hAd zA^hK_UpQJ~MW#tps*`_mo8ZGSQCj~B-;mhSGMwbo@oSULChD09%XLk^b_5UW#EDH! z&`B32esblL|HLyzO9r8TfLgI9J>F8OMk7JkTf#-V_3W&(;6b^pDV3PCUbRHSTKZ!S zhTcc00*q75Y?NA}lB5`n22zfYWl?=r#^hn>m@Jh1StTZC@i`gB6oW(^!bSx&Qv-}l z)IN{|-sG+dqwvHjxZTT9hG~QOy1DnKnNtg9q!pNjob&)B5T`|IVp?TM+kshOIy(=m zUG)|dU3#faaelLH!SaGHXy^Ic%kc?C#h81-1TIniFe?kc>5p+Zi7->ty1qWl9{pn$ zzV3c};*aY{HMBa6N&IOB8=rtgpp*mh8c*<*&oB$B-#>qRafz6LQ{;xL8hMF{kxP*y z36(SE%|$0}r+h904H||IZT|5MW^MN72^?f2`vG@}uu&0uZuNa^MtG$MTS`(K4Jiwn ziL)Qw2c!KfC<{|cy2x#1LFtsGZteJ>T&HL$jCbI81jGbv(2)QC?fD>`s&OjCkH7>> zImYcWc6k>yZwt(w?c_WVS2Q;-cO{emS5H{?sqy{t)@6N~_f@kEplxC=o_g`>AM4S< zH4}71NALATiA3rb|9A@lI+LWB)H4uyeCxvntwP+=qgA+wRcBd0zAj-f43VUmv@@vU zYu}?z?UZ7d9;?C*EvY%XU&-)s5Z(DB<)a153ySD`i4#~95H&9O8%< z=}GY3Jy_kfBZom5ZGV`f3o4V$%`oE*zqyIq#MKjxy=|(t-k7bH(0sQw;U6i#r9vQ+ zN>}s(?@(`9uw-KhlzbKSBG;@c{=)Gvf_g5@80H9bhRGkWW*GX30gF6nJ@lN({;_tO zFfq7ju(x_#@66)MPu&*Xfb=sTA6NQnak{t9p23bmVT;L$GHUDd%~A>1zr5TP!r_pP zBNcJl^_Nd~V<=|x5D}WjuY7(%uOodg_57D!-!E#29Z4|hH)Liqy6&&fkSBPVqNaJ zw5@6DqOJP<1rZ>qO19XB>_axd&!Q&_uU*y^q)O4@E<6?Z$)%P<{vJE-d-wryieOI= z;RX?$8Mrq+Fg4mEyujIDF z{_O*@j89(a`FC1-G~b%*zeJb4bJs+QY4+r;YItopfHc^MJ6To9nj)^4B`zb6oWbSG zj1~5-sadExdquCTfiS@hAhmEcIX5XbV7m9{G;eSo+%{HNTEB7IYzGcF;mBVp++uca zjWzmI1{KhHwc%PoSXstW!yXVRUypI;UY|crVn2@dG4{GuNPrM*&RE~J)`Q7m|Ck4` z5#+AtxskJ$E>$>CW$&0WlVOvK7mw^!VK?T~ z7(qMXKsa@A%;Y5Y_AsStD+&tc?ckR=RVAWT6+8H)S*l{;4mg)IO znr(dU4*wm1jY9U(i>XQb=^;C2wlJHg*4(3Lc+bGjMQO+RKe;rn3LOr_{1#RW|(Q#1$imc3T&Hq znP>3~wM8UZ?bQeS!(d+0!XNCP4$_H+_QK%G&zM9&%0C&T^WJGDAMhfBCp`a5zv0LD zqyn#$qT~j0au$#LszDUfCXBxTsVfq)@N1!IQ$G-gi+BHkdN-z|OHWNwj{AZVy7dqb zOeQODq6(d4N@gh(JbLlEM%4Qoo?>04&lfzFRoTRtCL1ijM(@1Y;On*EL>H%;j0>u6 z@}p9qG7-N!bCNxLqgO%J4nIAF&zc&%M z5Vu_=8lLO&NfRw>*0OJUZG505_IJdNxDe(kfD|JRv z6OM#uKViDZ1i2|^YB@7d|9(11>8({v_TImn0w21|&ED4azZKhFT;DyPBF-`t3ep6V zDdL)AC%haNUNEeT7b(*Jc_!G>STZ0tO6=!}8|k8bF?pGpdMf;6M76|h;jwJ*r3e0+Ij3`Wn43YvYKpV33-vrm{`Eh2*uLDvGlc5F%0+&E zWwNj`9B=3hvHM%8GYn+LT`8P=1rT4FB-2Hr$jysJ8Tk7_iY4vX!QH8HKycC;wxUFf zH3GI+ezTL#G+P|R2tLESPym4-P+?%Kc>^>i(w}YiaW|f$8j*p}y-&d4)2_N#$)`=f z*gmOqe6FIULr6)wuMzy6akat(V8U6Dzz40{d-YI(Xb zAUtfvJoD+jw5_cpWxu$--5aKJ8=m{^lK5AUSd@G-ZGB3L|E>O82S=MSJ1RbNkS#YC z&i8K}Hm+F3(3h4Nx844F@I#x$$2nN(5}w^|^Byb66IB;E$ct4}3jB zDt&k8%<@~YQl9h7@Y&&1#++Njx0c@?y2USU^3xB3ah0iY;#geEG+Px>=VSPJrq|$1 z>fu{Ji4-vOdpwVs z(}Ax$;!sH?SJF{7d^XPk6CUu!Y$x1aP9(4#XlZPiXvIyR);0D)dhBP1Z@1(rO7Y<3 z;Zs{#5zrRrB=Y~T>u!hA9-~8Vg=Uce>zy_?{Q@GQSU_}Eg?mPr#5BFJOr0ZdqnRX0 zVdd^nw|%R(D>L=DwGcIXUi;UY>Fp7`onbEbz*ZJ2ON{7I^n_hYFm+)win@3+>Sqkn z>i^OFrun<3-QCtUQWri(l_sNcFne|Mnc=gp+e2s0tGbH2_)a?mddwNL-3rKw!Dz~E zXW2FFr{qnJ-{}y{5kw|rOn@{bBO^Y}>hPE51wiEC8NZ@=l-{T5yQaUJ)-=^+HtlTY zieqV2N8NVx@hVE2P?Bs@T(5S4uusC0_|*7qHDRwd?POghDCyox9QQ$vX0s9~U08Z^ z!Ua)sm3W`VX57%24Y{te<3YVH*Tzkd>C2{YL`1*t>Q72=Lo&R?m;$2c@ywrqXUqVy zsI+`^C}@qM^5q2?1I?Ds}VN^Qk z!*%n1m=ZVo9^B)ay7$Zu(|nA&CU0}~pe@G7sBKcXsvQO#6b>=)HiY(5W9o~9QQ4Cd za#78WDzb6SVB2i7YdYLW!OIad#SgzNkJKg?Jf}okd@`*7wHiEI& zTR}*hn#{|!;pm`z?5{pn+suBpFH(!qvqnh92HIy_=^Ud%k7DHB+@Oq&8CGi(A%Co)# zg971{<0u{_zD|+hAB@AcxOMEnE4$+va|ttQlhZ?f3k-URF__Dg9ru9O2G@~@kXwAj z2o{CG?Gl0H5m}`S&+_fh@&(B$1ToV8@x<%RPP!3RypQhANhr3;b83DykV>0Q-+sP% zVdYA*RGkLyc9|LE`k~qia^VO{{V**oF4^}TnR)A%{?Xky+;qNyhfZyX7^wps8tvkk zD2J4XC`1tjqo24vrmCLox{i+mP#{ z+m_L4Z3T69D$~!vOf~pX3LI`9G-mW#dov;_pIB|AA3_9@H20s}YocvYMq2m;emrmA z#?Y)R&Ey)l(weIf`GE8Ltay%(uDD+4YB z#TVbLb4SqS1lf{weB({2QdlNC8X>0xLkfC-G8{Pg*cH)^8ooJmx&yL@#T4)`YUeHn zy~U+xq4jO#@asJ8IZt$F3xnYW0(WIqSJeIuM!Y83gYlLQLFE{$ouZ%T$)?yyRIOFK zSBl*oB8U{=WE5dJ7q#L3z7V)N(S6@@pCHELoQ0Ol;zJFi_U=K6neUFX{!-# z78-+#T~{V+H8|4sU9-bZDfM^O%@-nsswE1RT%m+vgv{e{r!yCG_ffJax}p$<8qi%d zsTsFCbtr8Grxr(dU>G=v5=IhAPNZ&4Jx31m^!P$##6YT;v?_Hd?sz1cO4N%1++zJK ze(BM5Xy|A0ZJz*2rY$|HZYtc*BkmV@BviJq0IaBocg~e zG}<6wCHl+nsh0ydM5g5%zUD4orj1Uw3;)L9tNYDW2+2LJXq{QK5d3OqE_ZN@2jl*s zUzdGE(+XXqiLKjKl#wXQje`2GC?s}mp(fnkKd$}Hzb9AkzpbKf>823q-pc$NDl}ie zZACMYa-GUwdk;a1?qyW#*|XJ?FJ5dPclNB5x~z+e&?j95r<7nHz00Ik1iT$eLM<=% zY*rV`Sk;r4eo`{ns0`W8SfZJ|;sbm5SHf2UE2G@gye$>=o)Ia!Q;D9G*VPWKm=1zc4-NnRNC_Kq7B_|ayJB}HD2 zJ~qjbNoJ4Z4OTzqUU_$5mYD7vw8oY;GsB;0H5c3p(QhgLot5s+%c#A{E|gwsfctu? zX@b0%Qqp=+JC zBfWY0kGQQ!K7e1&hX+WC5Gk6|^$3qt6BTj`^f*mSsXYhM{c~X3K7o|#D%e)IHSMhU z=n$@hCbglNXt3`RmSI>IO-keDW$sZHIZ?Xg%)KaB+kmm1oAP(<>M4ImfYcA|+I05& zI~&i{Mz)P&ESqlscy8mlf(Tf#2yH!+R>0kxdA2Y$^5z^;YQgQrXGhO25`P9rG#6F} zY_is8=3?U@@MlG1AEULXnEL2+g7t8OrZ}|#}%$9T@mVI2vu$7G^y9fntFaQ zXPumsncQ04mR#+g^c8=u!VwsrGx>|e9|!UaOglW`_~$)}8#Ix+N4 zex&VulLXqvn!PQ_$(3%E`}Xy59B`>AKiRZ@P<>xc^LPpCh|NNMO zv!~%i&6;7RlenXg>uh@Ox-WDm1m-kO&CZ#YlN0anYc%=#C4lpX4`}{h=o)S$$C8jL z4UPIiOxyI7Cl+`mQU4E56>H_s!DbLN^R|7Q7ei@x_Gq<2hRefR6B zN_X>|U{mKguiD?(y8kQ=L6e)fcDIb1FZ z5uRJ+c{$&oph?OKwM)}boQVJzBeW@6rgEW2rlhSnKziaRy&;hOjuyP%tY~gA%FZOa z1Cz?t3&%|5T)rthClB$TpC5gGeZTj@-uIxaW&SJp7snBNXAjz|%}w4$olo?j{TArj zN%c76!LX_@15=BjYF!-ewmgv2iISJtYwVlsVQgr>FlchZPN)zhsxJ~YJRH;mLsHOi z9G&EpM(U(V1(!_D)u!LQMxv;RIK@(#{W|MLF)(;r{e?81k(R}6R`voz%?tmu)a&Cs zV0e0L1PvJd^y%WUl6@eMWos$|pB5}m#{bb1v;dbh+o^!YfG!`ih4$^u(hf{PP7pD! z_m8b0N_>W3o`At`z*LY3lY3k;E)f08D$)Z<6|DKo)G|67M&l6+%Dd&EzRGrznE248 zaVf6+NNvdiStf96MjNk})YV+ItBOC<@YQWcYqNDJ0yr$?(HluA*0)2-{KGTY&~B;Za}v;WFi^c$VnS5CWg( zYKU>hhH_##>DZH054*K8Ffb<5Yz*1d>J_#K#$}y>T^e$f$L`xxGC&NH&yg(Jn2pgq zrw}#Q_hp@^u%!X0GZ-KKPJL*G{-dQ%j9#*r8_}<;^m_KWybdzK!dcD#BW)quf0AyW}Q*e1238Y;e2^>SA!%Xs!?TCZ+7>Tv+M2nTn|f;p9GH**!U--lS=bo4KE#iZ(p7W<^}2=t?hAz1CSQYLfvxE#)!Bn*7yng+I`U5c zX&s#ECjBUVN1{_JW?-596Jhhw1I0bIc+Yh<+%7Dxb=6Sy9t7<7TG&F0h6(3ca$!acPPfs@-S>yd}lHbpOhm z&*s(x-%LTdLaNQim*2p~?;V1q70iW`u|+gT=Vz3x`J*A*1K7xwFwFktutaT42R4{x zCf5bX92JedtW?97f{;}DH)jr=y`!bhBi7yNzh&a3tJn=o;A1FTD_t8|4pJ^%=$u+s zE|{5@d?%m5oFv!|@bufP9^3qJSEhbf+kSQ$gk_%FGi_S-fpfd+=4ZkGKA9E{%3H}r zC+f9g#%$m1{?KV1ojap433?hb0~nMLD=}j7NhrhG`mfL{BI3Pd_oAG%lfL zsZn?mxJpN`1Lg!S_J|hruh`|w4RH_ z$#8Ad+5sd7GCzPXs-b*(RF=^re7q<~+yv}LVl_>a+Du81H2(z?=I*iWJe5Sv(ZV;U zbljC=XBmr@yjZ_Gch)V}nY+;P)9n+5PacDPpC*p6wmy4Hh1cQ6Mqm8&-BODi}+PQTs?xMO%pz zSi?bbr~)!(VoD;>E9nNIfzE-(VWf8SwDD~)W*zSZ-ZPcpLYEl{eAY28``8!;ZE`W5 zUata1+@w(WGMo(er4GC-c6@AKll%m>kBwKrxgtuE%Qevai^s%5$+uGPrY?)Sj62Mjr^;5-jP@4U9e96pk0cAhfp;DGh?q2k=)2(6)umz0AczudeYfjGZMcyavl%;N%1q z=3n&uB_ioWVEYjWfT?R+1+A`Le(NYaudm?rK5W%j^o@ZD+(We!z(p`5=b@2z2}~2= z;}jR9R~ZL3IV%z^6wyQ)-1S0P~!LU_XJQ#C&FHZt^DVF z368xgaAH(_@;)YP)_M`IqE6?B0BD;FZ2|kkAMlKC@-H0}h|x~)sD^W%&l$w?T-{tG zBf-AL<_NmWT(l=l=^i5Na3Ydlsb1B89YZRU=nAJ$i8qXK8Q* zs)|Y)yqcHn-Thl)T!muH=!N^tnqZ%vBAmn0+iz==tQ;l_!%NgJbnG{esA`Gu>xx(2;a9cODtqwf!4bZ_J;p&59Z; zs`m_z3EScJBH>XBZw1>>?B}Dxsf(fhbfSTL)7aBY7YI@jpD}p^uMEjwQQWYarubV$ z``fg0UEL`LU1l-B!MdgKJE%6*6XxVI{bTnBP1bJziHDSbD4Urnc(Z#EJcVgX5u82p z#zIy>=!~O%PG{fIW_@V)ky#GMtRu~C(z_*8r#_zO!%Fruo-TcPMz*JZfjiNJGZ8}< z)bEj{C!i6{gCS!&KQwKo zOS{TXi2p7K`8RWI$O;s6woz0nDAUykI2f7|IybnH;N+k!eht@(C}ut;idho1CEZkf z-}j=q7-X|+O@E2FvKPylcksaj7??TN2m)ApM8>7{)gG%K2Lf3$@IlyNrJ&1tpNS)m z6xUsfdUd_?p%ZVzSjB?UM!rOfw&OFuG8U`r!Uw_?urcTRUkYX}R}ww(DE`LL80$2z zX|Yz=Sa;!)Ibcnpn2P|HaEg9ja?IyjjYbI+G-R{nJU= zZ>3B9u!Hl9r5bv5Z$A<|c?%Q&Qa0o3KMmMmB~cAu-0)QqxUJ*DLVHz{ZGC2O;bUgA zSB!yv3Dk@$x5Ui-Z2dMw-kSIH9gcCF0ZZ2DHzmxGLs<3wX*u0-8pypNI*~3!D*}A% zNNLC1(vJMtpt7{+5HnaMb&A)f9r!?M1g*y8xCLN_kutuss*NkYSm$j-NN@y!r*C;l z;#?t0Pc7u}ln!kAQHuD%u9XQlpCxPEm>UL#MPoN8l;~1(M47SOm~@u{OJhU0GKm=A zn3^&NA-`j0XEB=4T)lDKnN>tEJw#V)pBRaR_4k7~=H6nGIxwz~Z&_BfnS*dL@1I z>cppl@f}<^A`9v;rlc56$tfmNO0tO?gRJvk+?>XcvG?!-NDe=kuTaPT>iamIKu_hT=sz`TuzGGW^ zNfYqv8UVp6(sHHb?4fg4iq95Az|?t!FOi>O z-}*G}ha&Y(F$xgPtj=L3({oVHY6@`UV!gCGR`FKt@X~t5q#8AX6B%ITgzxB#>AUek z3j1;|{wom(rl7(POjX6|#ggRJ6$k+$iZtIRlq5pGNFY_Jko?~YLP|=N1>Y)T2fe|7 zmSXGhVNdE>*GKXL)A0A6qE20ho`KL$opuEp4;5d)KgBIjGr<07LY8*8gN0R*wN)6$ zD2vUZP{%Wx@xhYG#h49Qez4g?4X5UX9i2o<5@$-8C}`Su(|*Y})-6nHmx1mV?{T=lIymh}H_#~xn0bN^f-5dps|HQl%goC} zT+xf=GM5q~qmCRh^;QCd)8r`q7|!rcGh}}a z(D_8KRWf{-W*$&~AU#tZ8ub))>Pr_qi@{RKB}!e{<8c#y<}$m3(Rqf)!~M9>Ba_|2 z&^{{@g{WVWkB%cIi**tfK;3ie;+Wh_)#jl(yAuoz zaq7tZ3hNynbwLdzWNE@ij)E7JFRDvZA}i1UcsR9noJT@Sw*|4`UmS1SUf&zQD`t?C zY3_dc3b1=`LZTRT_*~Vj8tsQ|Z727P%fO(ImtGQS^Yt)Uk5p!Q&aM~_nrqq1=`F4t z--RXpAtnFAi0@vu$VcwREZEYL9yhY!Mi%t5IYJ~D^6|`Td(V%Yd+J}ybdpN+d;-am z9Q_>GR-10*aC*o@k z1TKlGIp78-9lO!Phv@?#?GPJUobqTe?ssh5HQMC6p1yxBbo3*IBzw$~z!41Ci-N(> z>r6y19>zIU0YFl#s5d!xr~9x+sT*}3-ll+{x{wZ82Pr_RdsJu!rIgwU(@S>r)S0kZ ztuSSsgkFBxeWqbj1{`AWUBXL$`k|p@b%$DWpjJ@YapzCzxUFKQmPW5r1Qx7~oE|O@ zcLb6ORN$RLU#;|q8!iV7M4YT9OZR9YAsMe9Y~_}R5o3L}J;8^eNrl!EYx5zg0x@fF zbfYh7WbQ--h$2g&l{q)h$vb6k04(?)#g3(|mc|VZ@p6m&EGiYzNTP)xr6KS+(#@;? zyF%GMkpBY3#}gS;ry^5$_bPw$LVjKS<>5tN6VLHr%oovAfU9myzD(7|e$WZ|B;__E z2TFRecB%5(VwD}IIy45GJS4&SyxotjI7r?>kEcUnCUb*y15>erMag>&aEo}{OPIuZ zo59Indt8=r91)HPB=S*e+x8is%v$9jRYnZf5x0g=b zhLZ^+D;dMtfTTBZHa`8hQvfg|qve0UeFIF7P!Yx8^(=mZ9r(t|0W*g&Jtz(Q4}uRQ zdKw?S@%w%mX8l89uQcK~(Fo-mxQo6f7E%G^v2ibNy?>mx<8ZvWZ043`fe^ZzeR?=G zLhHOYKE!}gkLc-^HL5`CRF9#U824x;1x4Wi(U5V4n9|J!%L@Wax%(*1DK){_i760D zFh*b59VM6h2-j&$ktzKmHz31w4`E6uAn{k)3h}~(G+3DO*6rYUQJJ(;*>5(|#qr9Q zr;bt@f(iwSL(mxn0Ku!l#rSLBFqUt{XObFUgKrm%v9X|3(d5hx3}pI~nv-tyi~+?F z7GfOkt4|nnm|Ag41w2Jsd=+yr$r4md&j>kL^QIJ$7n{fs?>#?oGNw}U-)6?ea%{4Z zyLry%xl@>@X@06Qe)4o=cs~|BZD9?C>!5Ake3S}J>X0~cw&vnjO9GzB?99T?#BBYy z7ugna9HK*$DwKgM#dZJ%(-1N&ddfis`LXIU)g0w^`P%j8GJPr6P2^#?%P7jzVG^m znY@_;5LSB$g==%f#yY4bh90GrS$w*98Gc*=WbENBG5ES^oF+)28GhTlF8d<>G*fQk+DgO2>p&xQ*`B*g3U zPIl`=LNJiIQe?S5F5SR?s4>9B6eSYX>@Ca> z4+!$f4>a33-jdc}>hy0mOJID<^Ke{ftnc{nHS*nH4voxA57cf#)EEFdGOmOz+w=O5 zgpu9jk~!h%emw@bwEEuj2t(=w*xL%dXA9A8^r#8D4$S#_@THXPUy7zFb!cu!WTuRX z9CfS-AMYFMj&lR0%MxYXJ{Xl1g~fIcW-XZ26OZ8{>>kc%3%4Dy;f_?DoE%mL34=gd zdwt+mVqA^=4{kf)ms2`~oBOGqNVZcR@8nL3m|OYq5j|Ck%kIT-eCdegi3UaGkc|QM z6?hK%U1`HG*rzAku{pJ-b~!SspRzz#KK{OzL_dFBUY<^b0~s2@5=mS$<^>o&!`T*3 zG|1B_9XPG?3kQigPLUP|+XN8G;_*BJT{PFjp$MYAC);j6wvo1A+i8+YvZF15u$sUM z7IaA@nnc`|Nf@A^s1#R|$4my68uT!C6^FxF$zgLQ0F@8~HZ>C#2icoc9H{hMUga6y z4%LH*UHR=tq7k}yMz^|UA0JS9*NtjBa=xGuNM9uovbz}6K4Nt>gfo(XHsnMMi~)zb zb(6eD!dj_yS3syv7yG+Y7>E9X1g%={4KivJlq06m3S@BEpAUe z?Rp+1(n5dXEdQ@3@)vQFG_K6tXYxe>PhX&4w_l)L2Q$WAuH>mJd6gnj3$H~}-%`!z zGYwi9^)TSXw&C}!T03+^r@PcznfQmTdw#)us+8~f?Mdk@VP;c>dhh7}cdRXA0Hb~j zJEi+&_fcu;cTEkF3kE~q zdJSMX@E}WkMO-GnDuzyVn8Muzq}m=)%nV3a0!@cuckli+)*)p@Jg6NjQ63nYmb@}s zDW)ZY3fsr!5~NJg2E&F4r0=Ek$eOiy) z`#|q2I+cez_S+s+riO!I<2g8CM5e&$y73tpm+Ib~b1C2kgO)1`C2AeZ$)u$T=-nFF z>{u-Rok{;u5asJ(uj7|j@YyhES4V_*tliyLQLL15wj3@j(Zkv2m`HmGmOaZ*S?jUf znJy_xMO5J>1|hD)vnUI2iwF~#ixlVnG|Q9 z(e|}Axtba`G0-@tX>Owygs$P%)@|6cGjdFL_?Sp%52~lWe5+XabbSo3X6YHcOe*w*cu z#fbEk{qjM8LO)>fH^flXeezi>${^_AE3RZR7?onfXxswA`Y?D(i=-vo04Z(&s%}R|Mbqe?7f&17 zu_%p}pJp_GHlQQQNb~z>J`*2A>t971pYFV+l@4mE^F&QI{Anlm33cM%3OHcPX!vf0h!>`SU-j<}hS*@jk` z?4{}Uewc7Z-$aZ$)L52goNX^nV}k#I@MZezOp|~{f$jLfu8!+tuf?UV3v7nl1M2q1 zo@<7r&^j=eslPq`MjYXe-HtHpXR&5_6!WHHG!B4vm^P($t=1G`&;t?VdNe`!3}y~r zA4@tTEYAh2AC)dafu(iniHpyKZio;wB5|T4GIR6yG0|=vSgk8a(2laqKmf}|x>Ckh zXK06eU=4PS`8ac7Q?F z+HAx9l`8#`Vnobke#)ej&qBfZo+{Gd%kiA&zEDJsP{dMt{NeS9Na)TAomK<47KmIS z^E^2Q-$=J)w3?wtb3M%YKll@zfL2ldM{tlw%f1>>F-UE`G{v zti7@&q2(Gal|bqT<6|qVVUjG03a>g!6(;Qp7PAlq637d$lO+M>z1$TN*zoF8`KM1z zTO#3_Wh#5tZ#s}%;?(eIS#eT(Go;Iu4!r%0z@57noOU$EdRZvMmV+MW$zs@67fQ^E zFvfzc$CHo^?kN!#+;>4OMRVowB@#s>&fFJ3(&1pB^}()QrU%v4GBF>88AX^oAB%t` z>W+r-zD(G>gdqJHnd2RIa3l($P&7Kq+_V9>-(O?)`{H?SANF57H)1^SdSne846}__ z4D(v>z0uaMF?|ih;ljNVgT@lo9epq%jmi95Y(X8+b=_Ays|Si^{I!~H+LfmjFj@Q$ z6uh?~87F`Rp!m$b%w>xS)E@8a>*(z5gN*vl4n2~UtX^E##?D{MUK@=XlX05qYUf~^ zwHF9xdSnNs)7~MN&%{?}n?1A1#?J3x{er89h4U!sLE;0P0W_1pfWbf16DvXsrqZr) za8Vo_^O}d>ke_^f=1i%aDHC~YjU-AYAP7%a%&c-TPd3Wp;atfLqEaw(IWphDb;uo@ zU?jCptedj?{3GmIv*()N1)lVOzaa0v;ifFdS}D7_*D_xPyprDGanQTzb|-9Hb8B$= z$Y4jcP3LPoKf&g!o$1*AXS`eDo0%kMJsB2O*3rT=R~{F)~Bjc&kf1c8QxpM?$+|8)Q9izZ4p=~rtv zVhP%=5j%Kq4G6SIIsVs>;U^O$L_H?E(ETh@s0SHQo{B9riHbw3K_m%(3=2uD)pZ05 z$h!Dll%)WfpYJ~Oo6mBrv@1VYq`#y8fN`M1IcZ~6dwNr?@|UaQ43k#lB$@cvLfoK=WlvC@VY zlp<{y3g3dRq2KI$U^hA>iE9A%AhLgpt9N-QF}GVi0wtv`&5e19^Q9#xxY(;}AB_zSx;dJgfKEP=^~X1eED&*>Cp&-tfsHSvpXHAb4K z$kLUir8lYc8;sYVPPf(m8C%4Ufw6ps1gT6i0%DPj?UdVIC%ul4zuSyR23cT0lNmQBg;K0=L{m^SVvb9(qif_pmi=D5Pi8CvxHk(b8@~3@fApIs6j@!jp8=I*3 zi4Yx;x|XqPTv~M?B4ehG+%EyIHwWq#OxsFR_jAZn-Qh2g>NuTC$=A;o+|&t~?OL=0)dLT>1vGx=*%IU^Oh_zXvB0;&M3*cSt^Z_$6HB zJ{$+V43MxE>cK9*N~DU7+TH++}y)+ zIYWXoPkbqQN*cDDBHfDgd^}a+z^Z2?`I7Zy8EaGp+(41ULVPbK741{AQ4FDCs)n}M zjwY3h?cH>;k1W*<{M)e+;}=O$+fKifWg-zjl#EDZ`$yFykj?cM$QZx%_|* zma&rS9#MwnVB~5R(?2nL4U1mh!}6I40K%2f79s$rw!^cS;=NdV7%f?jIqqT(_cfCM zvUP`goB7S8>Fo0Z1vqKCZHC7Tcx30%$Rx)cBT9>G+XIQOmV{X zy&KD|t@xYOk^OTPsjs;WF5FFka$i!=)54BC=pye zz{1{Q-NIa&o?;yeDerfNxiPK-BC!pDEbPH)H!VpPkf7?qQ8ykvtKT%|LC=X2w<`yk zI-1JwVkn8O%t2fhsKBL2OiF|{d{yP*%2(E^#jt6ohs;Ccq4pS03X>{Iruj@^xK}U1g-9EtYY!}>G=je{g2%L13f7h^?~nm;pM3Xzmgm51NH&) zfFD0%^qUChcob_H@aIL4`3GC!_Gx7~)=k-U{vmdaS+pQ{jfj5dhwNGGtzB72K7P2O zXGidSPx`y9jzQlbu}+Q-Ys0N>;L&4g8gtl?cY`FG=Ylqb*GwRbOHE;N1SB2#;lD;HRv@#nSG(D|y3C_)HMd3)OhX;C!KB;Tjs7~jdRxs4I@5_CNO37qOtjsXH`1D7r zc*aU_7E4i)m$%84vmSKsHe9x&Z9H_RdJbLjAxoisR(vAYXdIUNfx_SZkn`aNT)~vY z{)cfc7ev}+XH_6Dg4Vr*59_xLKNud?96o$y_%NJ}G}G_rk4IC1RFKjv^KyP$)bgZ+ zPd8lBPy#(5Hfq@>B9{<*KMw=|R=3B~jCN%MWWPx`y#nm%Qy|%Ibo+hX&SM zF2f*sPJh2o*5B^#mc&NA6^f&Y6hR>w#Z*oVil?!9CbR0SZYj=0Uee9FR#Z>VPcT-I za7uV%J%MQ^2<<#JclXX$79ET_*xGC6r{cz%>Q|=1Cx)4%4&$#pczFhA`3%nGgZx#Y z>L{jLM0;Z(c7L@qAh0B72{xp$-2z>+cTjozVtntPUjkDzIV9UN#(X%&?8#p2$2Udi z3g7^3Y&cHl{BQE%6n{_D9-jPPC3WU`en`(x@ylb6PWn&g)CSOf{{i&sBZw(^y@oN* z?Gnu_d{zvnhoY@IY(SvwNA?9I9?2a%m>n}%)vvO(tU-wHNVdf*+AZ(61V(Tmnh=$; zvBH1D;J$Ky##W2SZxHV5VMm*Y0fQ7Yb>jVkIR!u31f%Kg>dv#nXROmg52xWr!lL&8 zNlJ$`p!O1x3>4?Sm(0!Y35*FM6T}KvX3H7`-hY5g9=BPsq@Nr@KDKdpQ*b5?}%wT(oHJ>7Cyem0f3*?EMCe3?%RD(*j8iK<2qV~9x$K;%jVxXag^ zglonBDo{*qY1v%=GU~0V25S{P0=Ir$`DGb`5VA(hAO=^(l<{?|C&p<@5dv9%EuB~v zTw68;#+bbW96poA)ia)2I|w*g3E}HB$$;c%`*2%>nu*~JA?w2-f38`Ld}8tAJJCnY zT|BoRZ$JMfncS8$b!r5l6YaIAD-GEQ)g>Gjpta~Es#eX!1SYnLS5iI%6w zhM!_Hiw%|TxXxqRD6|tMEEmr#kH(IfSb%LZ*ldq^F_FdD*gJ=CfjZZ-@iAy3k~=Tc zEZiGSfzfDP4_-NI-&;EEs`p0-LY821zws?7Lr++Q)C8x5m+2Y#wB?U~Itaz}sAP;9 zK-DK4|DlPaEOb1B#oVyaB*oEG>C>4!6$8a&EJ|(rdBNJeHDI^YzzjhQ0SzEeR=@1P~0q6v4-N3q&kTvs$*uh?1Kl5F@qG;dQQaow)!bU345fwqu%ryA-=BQ_Q zZw|8tdsA?9QTW2nT#=Ap2WuT=2m~cYBC22IQUHFQ3jhFjqM*;$h`zM4?nIFi0a_{? zT~u)qzIu}1S?ip|6W16WbJkeJ^ibGZNt^$p{aC8l>CxY*#&>t7RqH=L9zE&EZKc&7 zh`|>M=a3w~-K1?18KDS!e_V2l4^RA8=#Y*42P8E3d5(91;OyfrHrozQutA9UcJD29 z#tpHUJNX=)KR^1NK@23GuE8@#f^wG!KE*Lnd6EZt%A=BO;!zWv*5@sUqg=Z4gSu(G zmA+Caj>KfVMBwHfV$5+Gri3RThUKgrGVuvwkY;7Y#D7(PvLF|4ukC8HKpW%U{4wga z)89-nQ%`WH?$u!-*p}@%&NDx=Ye zfNme6_`K09bxM2d{Oo76AphQhnm&1ZQC*z4&TRFJ&3r zOEy_=PEiho0wFNto4neN%u7KLL;zRqAAnmh4uR9t(v%gEWL^fXfmSm@D`g2*P13Ja zk$>r4J;>CMpA0T$C=W1V!|7O9qvLn52Sk1jep&=2Yz3jAr!^BL{Ev-x<%X>B19y8u z^s5tc%cPZlJ;z+|0RViZ{fmW+^`>}eHFGa?IlMDPc_dIiD^Nx)gNHu<0n0DV#tP9| zxb4RuYM$A2uBZ!3K;ZuFbqcFj6D@D6!a(}Jo0FqUN5OP&XWum+J$CG@N4j!)PsJ0z zg%8ZLA8%nn`G=oLX<X2XjS->c6_X%s%uVMr?aszn!!d4}M3USH0d{ zbJqDKcp~+QC1%^)ixZb^xwQPLElXD=w_LEPYL(wKzFm#q_`k5gTsrr%_%##X3%s&* z_4$c|mwi9@9c7C#MK%@SFLpFHF8Ji|5*Gj4V>e*R5s#P+Df&R6)G+rd+V!5;b9LSQ zKT=*8xc9Tye{ee^@cN9+37j`8^~D{J2(%EBrA(Mo$CTtPb54^qp1hctewtk7h}WVY?euY-k50wp&(PCJDM8d zgF`>IuB}^(^x}2PuKn9<8wxcumjFEnFLvE*+z`KJ<#t6V1%&QJ|GqZ7gf6HF-2bR@ z?pz+yoQ`QcQL~`VW{O+KenHt zl-HikQ}7Gv;w0CUUurZ%*N?JXA)5^YTz-XDFJ>+iv+8SDNb{h%dEP)Z-s#{py)QR`TdKi(jV z`E&utgdz4Rk9l^8VMR`MUZBTgavJ3UX~x9+ zyUDS&8aJi;oI)3`As+z~X^-sgL=kMyrE;u^e2Ho{xC(ha`~;%50^6X%3FgcH*TXdO z0}Z#beEUc3f*)Saf+FiI!kcS_(yLz_#-kjCx$Rbm9Cj22Yub~uqm#2k+k?YG`NT?S zQfn&$7NJ=Rt64hqW2A%QCBaF)O1vb&2O=b`Z6c_tGkKEz096xKu&~(}@|7+AP*zv@ zA~_3-$^WE8CEk&|fT3O2lS6$uIaWD)MIQ8;O5G%xIO**)ADV&P;o>Wl=Cg2c7Y*>9)jMhv1)rh4|&-?i=oXTBDDYAAgc&W>6eWYrQ_2v>NweWXD?qhaf>;?IU3_812Y9lm&o~GLpnZNs;N*u+)9`Gt_UdL$hF&8sq$>hh_4_;NAO@ zE3Dnt+8MR6a|sQ*s3d?>jwma0%d z$8-|UL#KRlBi|@A@*@=rg*vfMzBQ;2hccN{(9}!u>Z^6kGiR7}85tF2szlk-RpKXr zY+qr9MR&*`h;npIRYXjbKoEYZYslciw~uP5Yq?Y_quT&4{LGnB-htm%tbnN@CoC=X zw(?Y<^7iuO`n}i;SQ6I+ztSkap$i^8aEjDAN!Ho!amLi-c&k(k4^~C;GamgV<)0+m zKN#M?2&WXMjq@dp9%v%|5;M{Q0Bv$A*;u6IEtDWDbY@{^vGtA(8!Fk>+ZxkdlLMyt z2NS)?;%4=AD=u(v$DNH+{?9_3)Y0qKmr%0;9+pJ+&?Q*>?=kt22%Lu*Au^8Ij6KK5ssd~sf@;N{YO;c{A)P)U{U_VDKHHb z0zff|Aq@>!M7~jwn3v9u=tP{4E{*%6&(+TIW(T$A;!}a*_p+Jq$QgLqKAIhtg;fZ{ zDO(1)DB0>hCdPRV>VxUp%;ofySVoYJN?TIGBfNTJQxEM!MlJC6&Wu7-TbDgDd%JHr zwOhtsa3TC3AMXS|hD1y>N6C_cO8WyU@JA45ofED9f5+u(E?KIzrG2f!h52Z3 z^iufWz_PNkWi*m-YeGLWd5N1JjG95_p@oa#w!{)~Dl!f~P8eW}O2pvK1cT=TqB&D~ zMrmc%%n834grU_%;;h6>Wx!bcmd-PRa9NMBvm)7h*OaV`zR;b8M^m!r8MnUW`f7ho z^B?Mh9ZmIH;lDVSF>YB}n)ApqgMub%qiVgT89`*rBAlnCYjMT_bBsLw6B4x={6lDp z(!~+UurvJA7)wFMBzlhI=KkqDw?7f6TgAp){_99dqAD0{qSNM zX5-FF?)T~qWCX3kI#bNOUahIA zBl{3{?syK*8YLBLa^|zU178u#(5kr#ZUPaZHz_BHCYpDWkBdCHVO+)3xkwrJt!FoT zevU>gjk5B*b;4CmG>Wz;S&fim9vjhfn9OPT6eg3yLo|7EH05GV7?yZf`BewKm5=cd zc6S%&y$!1NV=md$BUWu%@X52dbHS$Hj+F(`^y%|TFmihHk>tA6 z>R1jIS{)|5?ADzUu3LAnD>ewcD<%%(vhJDPU#LE+lvXH`VE5;T*uKFCxEkV#`|$jI zOuilemH@fN9D z+|LI)wak$`kNKVi9SPbT4n&t2xM8y2qNJ`YE?V6Ig-r$tzRco4+}3eh05kxFTqA)t*`3>KqQ z>xMXWI)X-#d_g{=`et(p0F?W3E~akK?MI9dX|ZCGylmAxwQBC_9&P&+CWM892_&U4 znBj63k{!3`0A~VM+{3lTHyIaRP8n zN6TR~DkRCGjW+5_+$lx1q?`c?y`CSGX;G=G%__A8jEi;=9Vw!cr0h3H_*X0&^1){< zf(R6{2}dIOlOYs|^50pHWiFD+XAImvdc%#=fI)z9*<2BuDH5!%1>Sc*@%BI%Juj_n zKL&w;4HOaBfPz5G@%mUEkB1r9N|Q<`B1x}4#6-r7f=M88HtpU7;rKf=-9!I}TxdRf z3pq0EQbVq437>04#c$p!7sCy{p8A5@b>Riq)88IP8Wr`aid~TYP zeMNNTVa701m@}-X1<(!+*W-4W=g<{eFe4POH>nZyLxj3FPQ{ZmN@rX9GfH9K zj%hWy~~D5~-qc5kzCXvX5*W*o!7j-Jz);-B72uLpaT;c7!k zG#6wW4H@j=Mn=!H>bG+wQ;7^$iO5CBc6|vE)RC;~0X*AH$fT$CC_0 zHEh5#y(u6_56x5)sB%N(A)u!JxQaWY4?k06K|<-Cu0VH-HJ&cO=BrXtm>}83EQ{$IOj7HL2kR5?_z7yW=1i|6B^HQgM2k}nBsT*5J^TH=e>2t?UL<(d6kIb@f!K~9=!@XWzNfBug-*bAGve&s?SqZ&U zWy|WXCrV)h3qc7fF-zdqHO=1c|p=!6w3$}+Q>+Up)p z1jJj$MVRJHOFOjymGhTWkKHUj5>GBlSE!5GJ1<$o;$q6} z2A9id>@I2vPZ?&m8Tt$}bv?MXR!x?wn1PHoCnXJ2_Xo;0Q{Z9viscD)(e+dWE#_*5 zhc&=utk+;Pz$KN2B`A)|hWQ^_g$;>xB6{m?hXpcGfmUFMeDTX~`H8_N(k>tjHPnuUSu zV_FJnrGw6qlu!22R~r!n>1wzGwg!7?#f zI~~5o9|te#cf!?a^dwOzecq6;BMcXtl{8$mlTG^LRT4!Y__#}b^W<6+G2Tq49ighaRf7^rd+!l}6bHD}H+m`gszt&n}c zk3(~8oSu~ZpXRJ@B?AFD7R>w}wa{bhes*YC%FpC~rGXg_2HryCoLG43Px+~cSe%

    vs1n>{XU;AMS&r*-WBMk?R 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 E0Cn&6cK`qY literal 0 HcmV?d00001 diff --git a/res/themes/light/css/_fonts.scss b/res/themes/light/css/_fonts.scss index ac15847e44..ffd3dffee1 100644 --- a/res/themes/light/css/_fonts.scss +++ b/res/themes/light/css/_fonts.scss @@ -15,22 +15,22 @@ /* the 'src' links are relative to the bundle.css, which is in a subdirectory. */ @font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 400; - src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); } @font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 600; - src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); + font-family: 'Nunito'; + font-style: normal; + font-weight: 600; + src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); } @font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 700; - src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); + font-family: 'Nunito'; + font-style: normal; + font-weight: 700; + src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); } /* @@ -51,3 +51,13 @@ font-weight: 700; font-style: normal; } + +/* a COLR/CPAL version of Twemoji used for consistent cross-browser emoji + * taken from https://github.com/mozilla/twemoji-colr + * using the fix from https://github.com/mozilla/twemoji-colr/issues/50 to + * work on macOS + */ +@font-face { + font-family: "Twemoji Mozilla"; + src: url('$(res)/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2') format('woff2'); +} diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index d11dfebda3..c4de8ecdd3 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -1,10 +1,11 @@ // XXX: check this? /* Nunito lacks combining diacritics, so these will fall through to the next font. Helevetica's diacritics however do not combine - nicely with Open Sans (on OSX, at least) and result in a huge - horizontal mess. Arial empirically gets it right, hence prioritising - Arial here. */ -$font-family: 'Nunito', Arial, Helvetica, Sans-Serif; + nicely (on OSX, at least) and result in a huge horizontal mess. + Arial empirically gets it right, hence prioritising Arial here. */ +/* We fall through to Twemoji for emoji rather than falling through + to native Emoji fonts (if any) to ensure cross-browser consistency */ +$font-family: Nunito, Arial, Helvetica, Sans-Serif, 'Twemoji Mozilla'; // unified palette // try to use these colors when possible diff --git a/scripts/emoji-data-strip.js b/scripts/emoji-data-strip.js index 42bf2ac2de..2b9b67c859 100644 --- a/scripts/emoji-data-strip.js +++ b/scripts/emoji-data-strip.js @@ -1,4 +1,12 @@ #!/usr/bin/env node + +// This generates src/stripped-emoji.json as used by the EmojiProvider autocomplete +// provider. + +// FIXME: we no longer depends on emojione, so this generation script no longer +// works, but the expectation is that we will shift to using emojimart or +// similar as an emoji picker before this next needs to be run again. + const EMOJI_DATA = require('emojione/emoji.json'); const EMOJI_SUPPORTED = Object.keys(require('emojione').emojioneList); const fs = require('fs'); diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 1032c52e32..d53d8e4864 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -27,22 +27,18 @@ import linkifyMatrix from './linkify-matrix'; import _linkifyElement from 'linkifyjs/element'; import _linkifyString from 'linkifyjs/string'; import escape from 'lodash/escape'; -import emojione from 'emojione'; import classNames from 'classnames'; import MatrixClientPeg from './MatrixClientPeg'; import url from 'url'; -linkifyMatrix(linkify); +import EMOJIBASE from 'emojibase-data/en/compact.json'; +import EMOJI_REGEX from 'emojibase-regex'; -emojione.imagePathSVG = 'emojione/svg/'; -// Store PNG path for displaying many flags at once (for increased performance over SVG) -emojione.imagePathPNG = 'emojione/png/'; -// Use SVGs for emojis -emojione.imageType = 'svg'; +linkifyMatrix(linkify); // Anything outside the basic multilingual plane will be a surrogate pair const SURROGATE_PAIR_PATTERN = /([\ud800-\udbff])([\udc00-\udfff])/; -// And there a bunch more symbol characters that emojione has within the +// And there a bunch more symbol characters that emojibase has within the // BMP, so this includes the ranges from 'letterlike symbols' to // 'miscellaneous symbols and arrows' which should catch all of them // (with plenty of false positives, but that's OK) @@ -54,15 +50,13 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); // Regex pattern for whitespace characters const WHITESPACE_REGEX = new RegExp("\\s", "g"); -// And this is emojione's complete regex -const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi"); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet']; /* * Return true if the given string contains emoji - * Uses a much, much simpler regex than emojione's so will give false + * Uses a much, much simpler regex than emojibase's so will give false * positives, but useful for fast-path testing strings to see if they * need emojification. * unicodeToImage uses this function. @@ -71,73 +65,26 @@ export function containsEmoji(str) { return SURROGATE_PAIR_PATTERN.test(str) || SYMBOL_PATTERN.test(str); } -/* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js - * because we want to include emoji shortnames in title text - */ -function unicodeToImage(str, addAlt) { - if (addAlt === undefined) addAlt = true; - - let replaceWith; let unicode; let short; let fname; - const mappedUnicode = emojione.mapUnicodeToShort(); - - str = str.replace(emojione.regUnicode, function(unicodeChar) { - if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) { - // if the unicodeChar doesnt exist just return the entire match - return unicodeChar; - } else { - // get the unicode codepoint from the actual char - unicode = emojione.jsEscapeMap[unicodeChar]; - - short = mappedUnicode[unicode]; - fname = emojione.emojioneList[short].fname; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - const title = mappedUnicode[unicode]; - - if (addAlt) { - const alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode]; - replaceWith = `${alt}`; - } else { - replaceWith = ``; - } - return replaceWith; - } - }); - - return str; -} - /** * Returns the shortcode for an emoji character. * * @param {String} char The emoji character * @return {String} The shortcode (such as :thumbup:) */ -export function unicodeToShort(char) { - const unicode = emojione.jsEscapeMap[char]; - return emojione.mapUnicodeToShort()[unicode]; +export function unicodeToShortcode(char) { + const data = EMOJIBASE.find((e)=>{ e.unicode === char }); + return (data && data.shortcodes ? data.shortcodes[0] : ''; } /** - * Given one or more unicode characters (represented by unicode - * character number), return an image node with the corresponding - * emoji. + * Returns the unicode character for an emoji shortcode * - * @param alt {string} String to use for the image alt text - * @param useSvg {boolean} Whether to use SVG image src. If False, PNG will be used. - * @param unicode {integer} One or more integers representing unicode characters - * @returns A img node with the corresponding emoji + * @param {String} shortcode The shortcode (such as :thumbup:) + * @return {String} The emoji character */ -export function charactersToImageNode(alt, useSvg, ...unicode) { - const fileName = unicode.map((u) => { - return u.toString(16); - }).join('-'); - const path = useSvg ? emojione.imagePathSVG : emojione.imagePathPNG; - const fileType = useSvg ? 'svg' : 'png'; - return {alt}; +export function shortcodeToUnicode(shortcode) { + const data = EMOJIBASE.find((e)=>{ e.shortcodes && e.shortcodes.contains(shortcode) }); + return data.unicode; } export function processHtmlForSending(html: string): string { @@ -444,13 +391,10 @@ class TextHighlighter extends BaseHighlighter { * opts.disableBigEmoji: optional argument to disable the big emoji class. * opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing * opts.returnString: return an HTML string rather than JSX elements - * opts.emojiOne: optional param to do emojiOne (default true) * opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer */ export function bodyToHtml(content, highlights, opts={}) { const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body; - - const doEmojiOne = opts.emojiOne === undefined ? true : opts.emojiOne; let bodyHasEmoji = false; let sanitizeParams = sanitizeHtmlParams; @@ -481,28 +425,12 @@ export function bodyToHtml(content, highlights, opts={}) { if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody); strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(content.body) : content.body; - if (doEmojiOne) { - bodyHasEmoji = containsEmoji(isHtmlMessage ? formattedBody : content.body); - } + bodyHasEmoji = containsEmoji(isHtmlMessage ? formattedBody : content.body); // Only generate safeBody if the message was sent as org.matrix.custom.html if (isHtmlMessage) { isDisplayedWithHtml = true; safeBody = sanitizeHtml(formattedBody, sanitizeParams); - } else { - // ... or if there are emoji, which we insert as HTML alongside the - // escaped plaintext body. - if (bodyHasEmoji) { - isDisplayedWithHtml = true; - safeBody = sanitizeHtml(escape(strippedBody), sanitizeParams); - } - } - - // An HTML message with emoji - // or a plaintext message with emoji that was escaped and sanitized into - // HTML. - if (bodyHasEmoji) { - safeBody = unicodeToImage(safeBody); } } finally { delete sanitizeParams.textFilter; @@ -545,12 +473,6 @@ export function bodyToHtml(content, highlights, opts={}) { { strippedBody }; } -export function emojifyText(text, addAlt) { - return { - __html: unicodeToImage(escape(text), addAlt), - }; -} - /** * Linkifies the given string. This is a wrapper around 'linkifyjs/string'. * diff --git a/src/RichText.js b/src/RichText.js deleted file mode 100644 index 3e8f834da6..0000000000 --- a/src/RichText.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2015 - 2017 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2018 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 * as emojione from 'emojione'; - - -export function unicodeToEmojiUri(str) { - const mappedUnicode = emojione.mapUnicodeToShort(); - - // remove any zero width joiners/spaces used in conjugate emojis as the emojione URIs don't contain them - return str.replace(emojione.regUnicode, function(unicodeChar) { - if ((typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap))) { - // if the unicodeChar doesn't exist just return the entire match - return unicodeChar; - } else { - // get the unicode codepoint from the actual char - const unicode = emojione.jsEscapeMap[unicodeChar]; - - const short = mappedUnicode[unicode]; - const fname = emojione.emojioneList[short].fname; - - return emojione.imagePathSVG+fname+'.svg'+emojione.cacheBustParam; - } - }); -} diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 704cdbd55d..50da9266dd 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -19,7 +19,6 @@ limitations under the License. import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; -import {shortnameToUnicode, asciiRegexp, unicodeRegexp} from 'emojione'; import QueryMatcher from './QueryMatcher'; import sdk from '../index'; import {PillCompletion} from './Components'; @@ -27,7 +26,11 @@ import type {Completion, SelectionRange} from './Autocompleter'; import _uniq from 'lodash/uniq'; import _sortBy from 'lodash/sortBy'; import SettingsStore from "../settings/SettingsStore"; +import { shortcodeToUnicode } from './HtmlUtils'; +import UNICODE_REGEX from 'emojibase-regex'; +import EMOTICON_REGEX from 'emojibase-regex/emoticon'; +import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import EmojiData from '../stripped-emoji.json'; const LIMIT = 20; @@ -44,15 +47,15 @@ const CATEGORY_ORDER = [ 'modifier', ]; -// Match for ":wink:" or ascii-style ";-)" provided by emojione +// Match for ":wink:" or ascii-style ";-)" provided by emojibase // (^|\s|(emojiUnicode)) to make sure we're either at the start of the string or there's a // whitespace character or an emoji before the emoji. The reason for unicodeRegexp is // that we need to support inputting multiple emoji with no space between them. -const EMOJI_REGEX = new RegExp('(?:^|\\s|' + unicodeRegexp + ')(' + asciiRegexp + '|:[+-\\w]*:?)$', 'g'); +const EMOJI_REGEX = new RegExp('(?:^|\\s|' + UNICODE_REGEX + ')(' + EMOTICON_REGEX + '|:[+-\\w]*:?)$', 'g'); // We also need to match the non-zero-length prefixes to remove them from the final match, // and update the range so that we don't replace the whitespace or the previous emoji. -const MATCH_PREFIX_REGEX = new RegExp('(\\s|' + unicodeRegexp + ')'); +const MATCH_PREFIX_REGEX = new RegExp('(\\s|' + UNICODE_REGEX + ')'); const EMOJI_SHORTNAMES = Object.keys(EmojiData).map((key) => EmojiData[key]).sort( (a, b) => { @@ -101,8 +104,6 @@ export default class EmojiProvider extends AutocompleteProvider { return []; // don't give any suggestions if the user doesn't want them } - const EmojiText = sdk.getComponent('views.elements.EmojiText'); - let completions = []; const {command, range} = this.getCurrentCommand(query, selection); if (command) { @@ -133,12 +134,12 @@ export default class EmojiProvider extends AutocompleteProvider { completions = _sortBy(_uniq(completions), sorters); completions = completions.map((result) => { - const {shortname} = result; - const unicode = shortnameToUnicode(shortname); + const { shortname } = result; + const unicode = shortcodeToUnicode(shortname); return { completion: unicode, component: ( - { unicode }} /> + { unicode }} /> ), range, }; diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 1df0581b89..b30fc49c56 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -303,9 +303,7 @@ module.exports = React.createClass({ }, // return suitable content for the main (text) part of the status bar. - _getContent: function() { - const EmojiText = sdk.getComponent('elements.EmojiText'); - + _getContent: function() {s if (this._shouldShowConnectionError()) { return (

    diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 47de7c9dc4..5a6c5ca581 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -166,7 +166,6 @@ module.exports = React.createClass({ }, render: function() { - const EmojiText = sdk.getComponent('elements.EmojiText'); const imageUrl = this.state.imageUrls[this.state.urlsIndex]; const { @@ -178,13 +177,13 @@ module.exports = React.createClass({ if (imageUrl === this.state.defaultImageUrl) { const initialLetter = this._getInitialLetter(name); const textNode = ( - + ); const imgNode = ( - - { summaries.join(", ") } - + { summaries.join(", ") } ); }, diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js index 8482bce593..843bb29055 100644 --- a/src/components/views/groups/GroupInviteTile.js +++ b/src/components/views/groups/GroupInviteTile.js @@ -117,7 +117,6 @@ export default React.createClass({ render: function() { const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); - const EmojiText = sdk.getComponent('elements.EmojiText'); const groupName = this.props.group.name || this.props.group.groupId; const httpAvatarUrl = this.props.group.avatarUrl ? @@ -129,9 +128,9 @@ export default React.createClass({ 'mx_RoomTile_badgeShown': this.state.badgeHover || this.state.menuDisplayed, }); - const label = + const label =
    { groupName } - ; +
    ; const badgeEllipsis = this.state.badgeHover || this.state.menuDisplayed; const badgeClasses = classNames('mx_RoomTile_badge mx_RoomTile_highlight', { diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index a25d4271ed..34a7e139fd 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -180,7 +180,6 @@ module.exports = React.createClass({ this.props.groupMember.displayname || this.props.groupMember.userId ); - const EmojiText = sdk.getComponent('elements.EmojiText'); const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper'); return (
    @@ -189,7 +188,7 @@ module.exports = React.createClass({ { avatarElement } - { groupMemberName } +

    { groupMemberName }

    diff --git a/src/components/views/groups/GroupRoomInfo.js b/src/components/views/groups/GroupRoomInfo.js index df1803fa11..db060218d4 100644 --- a/src/components/views/groups/GroupRoomInfo.js +++ b/src/components/views/groups/GroupRoomInfo.js @@ -149,7 +149,6 @@ module.exports = React.createClass({ }, render: function() { - const EmojiText = sdk.getComponent('elements.EmojiText'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); @@ -221,7 +220,7 @@ module.exports = React.createClass({ { avatarElement } - { groupRoomName } +

    { groupRoomName }

    diff --git a/src/components/views/messages/ReactionsRowButtonTooltip.js b/src/components/views/messages/ReactionsRowButtonTooltip.js index 4f26cea708..5a71bbdf84 100644 --- a/src/components/views/messages/ReactionsRowButtonTooltip.js +++ b/src/components/views/messages/ReactionsRowButtonTooltip.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; import sdk from '../../../index'; -import { unicodeToShort } from '../../../HtmlUtils'; +import { unicodeToShortcode } from '../../../HtmlUtils'; import { _t } from '../../../languageHandler'; export default class ReactionsRowButtonTooltip extends React.PureComponent { @@ -45,7 +45,7 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent { const { name } = room.getMember(reactionEvent.getSender()); senders.push(name); } - const shortName = unicodeToShort(content) || content; + const shortName = unicodeToShortcode(content) || content; tooltipLabel =
    {_t( "reacted with %(shortName)s", { diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index 75898736f1..2ccf5a3315 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -95,7 +95,6 @@ export default React.createClass({ }, render() { - const EmojiText = sdk.getComponent('elements.EmojiText'); const {mxEvent} = this.props; const colorClass = getUserNameColorClass(mxEvent.getSender()); const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); @@ -117,7 +116,7 @@ export default React.createClass({ />; } - const nameElem = { name || '' }; + const nameElem = name || ''; // Name + flair const nameFlair = diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index ea7f634691..3d9807878d 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -463,7 +463,6 @@ module.exports = React.createClass({ }, render: function() { - const EmojiText = sdk.getComponent('elements.EmojiText'); const mxEvent = this.props.mxEvent; const content = mxEvent.getContent(); @@ -502,12 +501,12 @@ module.exports = React.createClass({ return ( *  - { name } - +   { body } { widgets } diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js index 6c87000615..c94e79f2d9 100644 --- a/src/components/views/messages/TextualEvent.js +++ b/src/components/views/messages/TextualEvent.js @@ -31,11 +31,10 @@ module.exports = React.createClass({ }, render: function() { - const EmojiText = sdk.getComponent('elements.EmojiText'); const text = TextForEvent.textForEvent(this.props.mxEvent); if (text == null || text.length === 0) return null; return ( - { text } +
    { text }
    ); }, }); diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index a19a4eaad0..466deeba28 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -256,8 +256,6 @@ export default class Autocomplete extends React.Component { } render() { - const EmojiText = sdk.getComponent('views.elements.EmojiText'); - let position = 1; const renderedCompletions = this.state.completions.map((completionResult, i) => { const completions = completionResult.completions.map((completion, i) => { @@ -282,7 +280,7 @@ export default class Autocomplete extends React.Component { return completions.length > 0 ? (
    - { completionResult.provider.getName() } +
    { completionResult.provider.getName() }
    { completionResult.provider.renderCompletions(completions) }
    ) : null; diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index d2ae6217b7..bfeeced339 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -111,7 +111,6 @@ const EntityTile = React.createClass({ let nameEl; const {name} = this.props; - const EmojiText = sdk.getComponent('elements.EmojiText'); if (!this.props.suppressOnHover) { const activeAgo = this.props.presenceLastActiveAgo ? (Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1; @@ -128,24 +127,24 @@ const EntityTile = React.createClass({ } nameEl = (
    - +
    { name } - +
    {presenceLabel}
    ); } else if (this.props.subtextLabel) { nameEl = (
    - +
    {name} - +
    {this.props.subtextLabel}
    ); } else { nameEl = ( - { name } +
    { name }
    ); } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index f38e3c3946..2267c942ba 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -674,14 +674,13 @@ module.exports = withMatrixClient(React.createClass({ switch (this.props.tileShape) { case 'notif': { - const EmojiText = sdk.getComponent('elements.EmojiText'); const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId()); return (
    { avatar } diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index b9eea2b455..3c098b3d7a 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -978,7 +978,6 @@ module.exports = withMatrixClient(React.createClass({ } const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); - const EmojiText = sdk.getComponent('elements.EmojiText'); let backButton; if (this.props.member.roomId) { @@ -993,7 +992,7 @@ module.exports = withMatrixClient(React.createClass({
    { backButton } { e2eIconElement } - { memberName } +

    { memberName }

    { avatarElement }
    diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index e54ddd6787..536c077a06 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -51,10 +51,9 @@ import ContentMessages from '../../../ContentMessages'; import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix'; -import { - asciiRegexp, unicodeRegexp, shortnameToUnicode, - asciiList, mapUnicodeToShort, toShort, -} from 'emojione'; +import EMOJIBASE from 'emojibase-data/en/compact.json'; +import EMOTICON_REGEX from 'emojibase-regex/emoticon'; + import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import {makeUserPermalink} from "../../../matrix-to"; import ReplyPreview from "./ReplyPreview"; @@ -63,9 +62,7 @@ import ReplyThread from "../elements/ReplyThread"; import {ContentHelpers} from 'matrix-js-sdk'; import AccessibleButton from '../elements/AccessibleButton'; -const EMOJI_UNICODE_TO_SHORTNAME = mapUnicodeToShort(); -const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + asciiRegexp + ')\\s$'); -const EMOJI_REGEX = new RegExp(unicodeRegexp, 'g'); +const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX + ')\\s$'); const TYPING_USER_TIMEOUT = 10000; const TYPING_SERVER_TIMEOUT = 30000; @@ -273,9 +270,8 @@ export default class MessageComposerInput extends React.Component { case 'emoji': // XXX: apparently you can't return plain strings from serializer rules // until https://github.com/ianstormtaylor/slate/pull/1854 is merged. - // So instead we temporarily wrap emoji from RTE in an arbitrary tag - // (). would be nicer, but in practice it causes CSS issues. - return { obj.data.get('emojiUnicode') }; + // So instead we temporarily wrap emoji from RTE in a span. + return { obj.data.get('emojiUnicode') }; } return this.renderNode({ node: obj, @@ -375,7 +371,6 @@ export default class MessageComposerInput extends React.Component { const html = HtmlUtils.bodyToHtml(payload.event.getContent(), null, { forComposerQuote: true, returnString: true, - emojiOne: false, }); const fragment = this.html.deserialize(html); // FIXME: do we want to put in a permalink to the original quote here? @@ -540,10 +535,7 @@ export default class MessageComposerInput extends React.Component { // The first matched group includes just the matched plaintext emoji const emojiMatch = REGEX_EMOJI_WHITESPACE.exec(text.slice(0, currentStartOffset)); if (emojiMatch) { - // plaintext -> hex unicode - const emojiUc = asciiList[emojiMatch[1]]; - // hex unicode -> shortname -> actual unicode - const unicodeEmoji = shortnameToUnicode(EMOJI_UNICODE_TO_SHORTNAME[emojiUc]); + const unicodeEmoji = EMOJIBASE.find(e => e.emoticon && e.emoticon.contains(emoijMatch[1])); const range = Range.create({ anchor: { @@ -561,54 +553,6 @@ export default class MessageComposerInput extends React.Component { } } - // emojioneify any emoji - let foundEmoji; - do { - foundEmoji = false; - - for (const node of editorState.document.getTexts()) { - if (node.text !== '' && HtmlUtils.containsEmoji(node.text)) { - let match; - EMOJI_REGEX.lastIndex = 0; - while ((match = EMOJI_REGEX.exec(node.text)) !== null) { - const range = Range.create({ - anchor: { - key: node.key, - offset: match.index, - }, - focus: { - key: node.key, - offset: match.index + match[0].length, - }, - }); - const inline = Inline.create({ - type: 'emoji', - data: { emojiUnicode: match[0] }, - }); - change = change.insertInlineAtRange(range, inline); - editorState = change.value; - - // if we replaced an emoji, start again looking for more - // emoji in the new editor state since doing the replacement - // will change the node structure & offsets so we can't compute - // insertion ranges from node.key / match.index anymore. - foundEmoji = true; - break; - } - } - } - } while (foundEmoji); - - // work around weird bug where inserting emoji via the macOS - // emoji picker can leave the selection stuck in the emoji's - // child text. This seems to happen due to selection getting - // moved in the normalisation phase after calculating these changes - if (editorState.selection.anchor.key && - editorState.document.getParent(editorState.selection.anchor.key).type === 'emoji') { - change = change.moveToStartOfNextText(); - editorState = change.value; - } - if (this.props.onInputStateChanged && editorState.blocks.size > 0) { let blockType = editorState.blocks.first().type; // console.log("onInputStateChanged; current block type is " + blockType + " and marks are " + editorState.activeMarks); @@ -1295,7 +1239,7 @@ export default class MessageComposerInput extends React.Component { // Move selection to the end of the selected history const change = editorState.change().moveToEndOfNode(editorState.document); - // We don't call this.onChange(change) now, as fixups on stuff like emoji + // We don't call this.onChange(change) now, as fixups on stuff like pills // should already have been done and persisted in the history. editorState = change.value; @@ -1473,20 +1417,8 @@ export default class MessageComposerInput extends React.Component { ; } } - case 'emoji': { - const { data } = node; - const emojiUnicode = data.get('emojiUnicode'); - const uri = RichText.unicodeToEmojiUri(emojiUnicode); - const shortname = toShort(emojiUnicode); - const className = classNames('mx_emojione', { - mx_emojione_selected: isSelected, - }); - const style = {}; - if (props.selected) style.border = '1px solid blue'; - return {; - } + case 'emoji': + return data.get('emojiUnicode'); } }; diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 7599c5c308..3b7874a875 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -66,13 +66,12 @@ export default class ReplyPreview extends React.Component { if (!this.state.event) return null; const EventTile = sdk.getComponent('rooms.EventTile'); - const EmojiText = sdk.getComponent('views.elements.EmojiText'); return
    - +
    { '💬 ' + _t('Replying') } - +
    diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 435b41f828..a40746dd04 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -147,7 +147,6 @@ module.exports = React.createClass({ render: function() { const RoomAvatar = sdk.getComponent("avatars.RoomAvatar"); - const EmojiText = sdk.getComponent('elements.EmojiText'); let searchStatus = null; let cancelButton = null; @@ -191,10 +190,10 @@ module.exports = React.createClass({ roomName = this.props.room.name; } - const emojiTextClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint }); + const textClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint }); const name =
    - { roomName } +
    { roomName }
    { searchStatus }
    ; diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 93b4a59fca..f67a7d55c7 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -342,7 +342,6 @@ module.exports = React.createClass({ badge =
    { badgeContent }
    ; } - const EmojiText = sdk.getComponent('elements.EmojiText'); let label; let subtextLabel; let tooltip; @@ -354,14 +353,7 @@ module.exports = React.createClass({ }); subtextLabel = subtext ? { subtext } : null; - - if (this.state.selected) { - const nameSelected = { name }; - - label =
    { nameSelected }
    ; - } else { - label = { name }; - } + label =
    { nameSelected }
    ; } else if (this.state.hover) { const Tooltip = sdk.getComponent("elements.Tooltip"); tooltip = ; diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index eb5e14876d..08fc6d2c70 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -212,15 +212,13 @@ module.exports = React.createClass({ return (
    ); } - const EmojiText = sdk.getComponent('elements.EmojiText'); - return (
  • { this._renderTypingIndicatorAvatars(usersTyping, this.props.whoIsTypingLimit) }
    - { typingString } + { typingString }
  • ); diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/KeyBackupPanel.js index 2ba05a0e6b..d8ed959dae 100644 --- a/src/components/views/settings/KeyBackupPanel.js +++ b/src/components/views/settings/KeyBackupPanel.js @@ -174,14 +174,13 @@ export default class KeyBackupPanel extends React.PureComponent { } else if (this.state.loading) { return ; } else if (this.state.backupInfo) { - const EmojiText = sdk.getComponent('elements.EmojiText'); let clientBackupStatus; let restoreButtonCaption = _t("Restore from Backup"); if (MatrixClientPeg.get().getKeyBackupEnabled()) { clientBackupStatus =

    {encryptedMessageAreEncrypted}

    -

    {_t("This device is backing up your keys. ")}

    +

    {_t("This device is backing up your keys. ")}✅

    ; } else { clientBackupStatus =
    diff --git a/src/components/views/verification/VerificationShowSas.js b/src/components/views/verification/VerificationShowSas.js index a2531800e5..e7846a0199 100644 --- a/src/components/views/verification/VerificationShowSas.js +++ b/src/components/views/verification/VerificationShowSas.js @@ -36,7 +36,6 @@ export default class VerificationShowSas extends React.Component { render() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const EmojiText = sdk.getComponent('views.elements.EmojiText'); let sasDisplay; let sasCaption; @@ -44,7 +43,7 @@ export default class VerificationShowSas extends React.Component { const emojiBlocks = this.props.sas.emoji.map( (emoji, i) =>
    - {emoji[0]} + { emoji[0] }
    {_t(capFirst(emoji[1]))} From a8297a7698f0e93df3c8acc93446e5bd2a2adc29 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 16:11:12 +0100 Subject: [PATCH 107/273] fix build --- package.json | 2 +- src/HtmlUtils.js | 2 +- src/autocomplete/EmojiProvider.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 1 - yarn.lock | 13 +++++++++---- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d5cad9dc0d..ac08372843 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "commonmark": "^0.28.1", "counterpart": "^0.18.0", "emojibase-data": "^4.0.0", - "emojibase-regex": "^4.0.0", + "emojibase-regex": "^3.0.0", "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index d53d8e4864..5e6199579a 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -73,7 +73,7 @@ export function containsEmoji(str) { */ export function unicodeToShortcode(char) { const data = EMOJIBASE.find((e)=>{ e.unicode === char }); - return (data && data.shortcodes ? data.shortcodes[0] : ''; + return (data && data.shortcodes ? data.shortcodes[0] : ''); } /** diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 50da9266dd..95654e403d 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -26,7 +26,7 @@ import type {Completion, SelectionRange} from './Autocompleter'; import _uniq from 'lodash/uniq'; import _sortBy from 'lodash/sortBy'; import SettingsStore from "../settings/SettingsStore"; -import { shortcodeToUnicode } from './HtmlUtils'; +import { shortcodeToUnicode } from '../HtmlUtils'; import UNICODE_REGEX from 'emojibase-regex'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 536c077a06..1f71855fa5 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -40,7 +40,6 @@ import Analytics from '../../../Analytics'; import dis from '../../../dispatcher'; -import * as RichText from '../../../RichText'; import * as HtmlUtils from '../../../HtmlUtils'; import Autocomplete from './Autocomplete'; import {Completion} from "../../../autocomplete/Autocompleter"; diff --git a/yarn.lock b/yarn.lock index 22072971eb..115aa73f45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2576,10 +2576,15 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== -emojione@2.2.7: - version "2.2.7" - resolved "https://registry.yarnpkg.com/emojione/-/emojione-2.2.7.tgz#46457cf6b9b2f8da13ae8a2e4e547de06ee15e96" - integrity sha1-RkV89rmy+NoTroouTlR94G7hXpY= +emojibase-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/emojibase-data/-/emojibase-data-4.0.0.tgz#3feb3e5bb5e5f2b6373b183b0f038c60889a9e29" + integrity sha512-Yi4A1IxB7iZ+09Wqr2BEpHSQfugc5I8G+wckDOhCia0F7oOdErf/85jCwbpRQy7xtBbvlyS3xQrYedSeQot5Og== + +emojibase-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojibase-regex/-/emojibase-regex-3.0.0.tgz#fc7a17aa20584df5a73619f06ac236b8a5bb452d" + integrity sha512-iNDkbtn8UxKTxjIlvHLqfXovZaIulnuuyo2/emU+ZlPr2OmzxGfiDI+iIQ3WWqdlA6lgjrPNWgpbytt4lnJYrg== emojis-list@^2.0.0: version "2.1.0" From c63419f8a23bba911abb0ef005f81ade184bf60f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 16:31:25 +0100 Subject: [PATCH 108/273] typos --- src/components/structures/RoomStatusBar.js | 2 +- src/components/views/rooms/RoomTile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index b30fc49c56..7ef080e235 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -303,7 +303,7 @@ module.exports = React.createClass({ }, // return suitable content for the main (text) part of the status bar. - _getContent: function() {s + _getContent: function() { if (this._shouldShowConnectionError()) { return (
    diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index f67a7d55c7..1c98b97559 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -353,7 +353,7 @@ module.exports = React.createClass({ }); subtextLabel = subtext ? { subtext } : null; - label =
    { nameSelected }
    ; + label =
    { name }
    ; } else if (this.state.hover) { const Tooltip = sdk.getComponent("elements.Tooltip"); tooltip = ; From 54cea146e86b7535dfe18d434ab1543d0da702c7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 16:48:15 +0100 Subject: [PATCH 109/273] fix ugly formatting --- src/HtmlUtils.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 5e6199579a..0fedd6a3e3 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -455,11 +455,13 @@ export function bodyToHtml(content, highlights, opts={}) { contentBodyTrimmed = contentBodyTrimmed.replace(ZWJ_REGEX, ''); const match = EMOJI_REGEX.exec(contentBodyTrimmed); - emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length + emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length && // Prevent user pills expanding for users with only emoji in // their username - && (content.formatted_body == undefined - || !content.formatted_body.includes("https://matrix.to/")); + ( + content.formatted_body == undefined || + !content.formatted_body.includes("https://matrix.to/") + ); } const className = classNames({ From 24b03374acb06c6e49656d4286a4bd32c4eda6c7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 16:48:29 +0100 Subject: [PATCH 110/273] moar lineheight needed for big twemoji --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 8f67069c82..c77a359f10 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -108,7 +108,7 @@ limitations under the License. /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { font-size: 48px ! important; - line-height: 48px ! important; + line-height: 52px ! important; } /* this is used for the tile for the event which is selected via the URL. From 9f70bf3aecf91b3fde7f9f67a599c97ff5268bed Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 17:06:21 +0100 Subject: [PATCH 111/273] fix bigemoji --- src/HtmlUtils.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 0fedd6a3e3..8fae992268 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -32,7 +32,7 @@ import MatrixClientPeg from './MatrixClientPeg'; import url from 'url'; import EMOJIBASE from 'emojibase-data/en/compact.json'; -import EMOJI_REGEX from 'emojibase-regex'; +import EMOJIBASE_REGEX from 'emojibase-regex'; linkifyMatrix(linkify); @@ -50,6 +50,8 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); // Regex pattern for whitespace characters const WHITESPACE_REGEX = new RegExp("\\s", "g"); +const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX})+$`, "i"); + const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet']; @@ -442,7 +444,6 @@ export function bodyToHtml(content, highlights, opts={}) { let emojiBody = false; if (!opts.disableBigEmoji && bodyHasEmoji) { - EMOJI_REGEX.lastIndex = 0; let contentBodyTrimmed = strippedBody !== undefined ? strippedBody.trim() : ''; // Ignore spaces in body text. Emojis with spaces in between should @@ -454,7 +455,7 @@ export function bodyToHtml(content, highlights, opts={}) { // presented as large. contentBodyTrimmed = contentBodyTrimmed.replace(ZWJ_REGEX, ''); - const match = EMOJI_REGEX.exec(contentBodyTrimmed); + const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed); emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length && // Prevent user pills expanding for users with only emoji in // their username From 069a5a9546b66b711baa804d0d8e5a070f97b99a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 17:11:41 +0100 Subject: [PATCH 112/273] remove obsolete emojione css --- res/css/_common.scss | 9 --------- res/css/views/rooms/_EventTile.scss | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index d46f38bddb..25c0c8a8b5 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -445,15 +445,6 @@ textarea { background-color: $primary-bg-color; } -.mx_emojione { - height: 1em; - vertical-align: middle; -} - -.mx_emojione_selected { - background-color: $accent-color; -} - ::-moz-selection { background-color: $accent-color; color: $selection-fg-color; diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index c77a359f10..d3e2684d4a 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -149,8 +149,7 @@ limitations under the License. } .mx_EventTile_sending .mx_UserPill, -.mx_EventTile_sending .mx_RoomPill, -.mx_EventTile_sending .mx_emojione { +.mx_EventTile_sending .mx_RoomPill { opacity: 0.5; } From e48cc44cbac31bccd052fea0a267b862e93b9288 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 17:41:18 +0100 Subject: [PATCH 113/273] apply monospace font correctly --- res/css/_common.scss | 4 ++++ res/css/views/rooms/_EventTile.scss | 1 + res/themes/light/css/_light.scss | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 25c0c8a8b5..b96836a2e1 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -32,6 +32,10 @@ body { margin: 0px; } +code { + font-family: $monospace-font-family; +} + .error, .warning { color: $warning-color; } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index d3e2684d4a..173b6db536 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -411,6 +411,7 @@ limitations under the License. .mx_EventTile_content .markdown-body { pre, code { + font-family: $monospace-font-family ! important; // deliberate constants as we're behind an invert filter color: #333; } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index c4de8ecdd3..03b9e328bc 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -7,6 +7,10 @@ to native Emoji fonts (if any) to ensure cross-browser consistency */ $font-family: Nunito, Arial, Helvetica, Sans-Serif, 'Twemoji Mozilla'; +// XXX: In theory this should be Fira, but it's a bit ugly. +// TODO: make it consistent cross-browser +$monospace-font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace, 'Twemoji Mozilla'; + // unified palette // try to use these colors when possible $accent-color: #03b381; From 497be91c4dfeb3a122e5cf6742a7555cb45eee05 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 17:53:36 +0100 Subject: [PATCH 114/273] combine regexps correctly --- src/HtmlUtils.js | 2 +- src/autocomplete/EmojiProvider.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 8fae992268..68a83bfd35 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -50,7 +50,7 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); // Regex pattern for whitespace characters const WHITESPACE_REGEX = new RegExp("\\s", "g"); -const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX})+$`, "i"); +const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})+$`, 'i'); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 95654e403d..c90343ca18 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -51,11 +51,11 @@ const CATEGORY_ORDER = [ // (^|\s|(emojiUnicode)) to make sure we're either at the start of the string or there's a // whitespace character or an emoji before the emoji. The reason for unicodeRegexp is // that we need to support inputting multiple emoji with no space between them. -const EMOJI_REGEX = new RegExp('(?:^|\\s|' + UNICODE_REGEX + ')(' + EMOTICON_REGEX + '|:[+-\\w]*:?)$', 'g'); +const EMOJI_REGEX = new RegExp('(?:^|\\s|' + UNICODE_REGEX.source + ')(' + EMOTICON_REGEX.source + '|:[+-\\w]*:?)$', 'g'); // We also need to match the non-zero-length prefixes to remove them from the final match, // and update the range so that we don't replace the whitespace or the previous emoji. -const MATCH_PREFIX_REGEX = new RegExp('(\\s|' + UNICODE_REGEX + ')'); +const MATCH_PREFIX_REGEX = new RegExp('(\\s|' + UNICODE_REGEX.source + ')'); const EMOJI_SHORTNAMES = Object.keys(EmojiData).map((key) => EmojiData[key]).sort( (a, b) => { From dbc6815abfa5082724bc8901c4a94f1723b32fd3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 20:48:18 +0100 Subject: [PATCH 115/273] make EmojiProvider and stripped-emoji.json work --- res/themes/light/css/_light.scss | 4 +- scripts/emoji-data-strip.js | 31 ++++++-------- src/HtmlUtils.js | 13 +++--- src/autocomplete/AutocompleteProvider.js | 1 + src/autocomplete/EmojiProvider.js | 41 +++++-------------- .../views/rooms/MessageComposerInput.js | 10 ++--- src/stripped-emoji.json | 2 +- 7 files changed, 39 insertions(+), 63 deletions(-) diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 03b9e328bc..d8f36910a2 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -5,11 +5,11 @@ Arial empirically gets it right, hence prioritising Arial here. */ /* We fall through to Twemoji for emoji rather than falling through to native Emoji fonts (if any) to ensure cross-browser consistency */ -$font-family: Nunito, Arial, Helvetica, Sans-Serif, 'Twemoji Mozilla'; +$font-family: Nunito, 'Twemoji Mozilla', Arial, Helvetica, Sans-Serif; // XXX: In theory this should be Fira, but it's a bit ugly. // TODO: make it consistent cross-browser -$monospace-font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace, 'Twemoji Mozilla'; +$monospace-font-family: Consolas, 'Liberation Mono', Courier, 'Twemoji Mozilla', monospace; // unified palette // try to use these colors when possible diff --git a/scripts/emoji-data-strip.js b/scripts/emoji-data-strip.js index 2b9b67c859..1c3738cab1 100644 --- a/scripts/emoji-data-strip.js +++ b/scripts/emoji-data-strip.js @@ -3,34 +3,27 @@ // This generates src/stripped-emoji.json as used by the EmojiProvider autocomplete // provider. -// FIXME: we no longer depends on emojione, so this generation script no longer -// works, but the expectation is that we will shift to using emojimart or -// similar as an emoji picker before this next needs to be run again. +const EMOJIBASE = require('emojibase-data/en/compact.json'); -const EMOJI_DATA = require('emojione/emoji.json'); -const EMOJI_SUPPORTED = Object.keys(require('emojione').emojioneList); const fs = require('fs'); -const output = Object.keys(EMOJI_DATA).map( - (key) => { - const datum = EMOJI_DATA[key]; +const output = EMOJIBASE.map( + (datum) => { const newDatum = { - name: datum.name, - shortname: datum.shortname, - category: datum.category, - emoji_order: datum.emoji_order, + name: datum.annotation, + shortname: `:${datum.shortcodes[0]}:`, + category: datum.group, + emoji_order: datum.order, }; - if (datum.aliases.length > 0) { - newDatum.aliases = datum.aliases; + if (datum.shortcodes.length > 1) { + newDatum.aliases = datum.shortcodes.slice(1).map(s => `:${s}:`); } - if (datum.aliases_ascii.length > 0) { - newDatum.aliases_ascii = datum.aliases_ascii; + if (datum.emoticon) { + newDatum.aliases_ascii = [ datum.emoticon ]; } return newDatum; } -).filter((datum) => { - return EMOJI_SUPPORTED.includes(datum.shortname); -}); +); // Write to a file in src. Changes should be checked into git. This file is copied by // babel using --copy-files diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 68a83bfd35..d89cb3490b 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -74,22 +74,23 @@ export function containsEmoji(str) { * @return {String} The shortcode (such as :thumbup:) */ export function unicodeToShortcode(char) { - const data = EMOJIBASE.find((e)=>{ e.unicode === char }); - return (data && data.shortcodes ? data.shortcodes[0] : ''); + const data = EMOJIBASE.find(e => e.unicode === char); + return (data && data.shortcodes ? `:${data.shortcodes[0]}:` : ''); } /** * Returns the unicode character for an emoji shortcode * * @param {String} shortcode The shortcode (such as :thumbup:) - * @return {String} The emoji character + * @return {String} The emoji character; null if none exists */ export function shortcodeToUnicode(shortcode) { - const data = EMOJIBASE.find((e)=>{ e.shortcodes && e.shortcodes.contains(shortcode) }); - return data.unicode; + shortcode = shortcode.slice(1, shortcode.length - 1); + const data = EMOJIBASE.find(e => e.shortcodes && e.shortcodes.includes(shortcode)); + return data ? data.unicode : null; } -export function processHtmlForSending(html: string): string { +export function processHtmlForSending (html: string): string { const contentDiv = document.createElement('div'); contentDiv.innerHTML = html; diff --git a/src/autocomplete/AutocompleteProvider.js b/src/autocomplete/AutocompleteProvider.js index 98ae83c526..3a561ffd0a 100644 --- a/src/autocomplete/AutocompleteProvider.js +++ b/src/autocomplete/AutocompleteProvider.js @@ -61,6 +61,7 @@ export default class AutocompleteProvider { let match; while ((match = commandRegex.exec(query)) != null) { + console.log('Matched ' + JSON.stringify(match)); const start = match.index; const end = start + match[0].length; if (selection.start <= end && selection.end >= start) { diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index c90343ca18..5846d319b2 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -34,35 +34,16 @@ import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import EmojiData from '../stripped-emoji.json'; const LIMIT = 20; -const CATEGORY_ORDER = [ - 'people', - 'food', - 'objects', - 'activity', - 'nature', - 'travel', - 'flags', - 'regional', - 'symbols', - 'modifier', -]; -// Match for ":wink:" or ascii-style ";-)" provided by emojibase -// (^|\s|(emojiUnicode)) to make sure we're either at the start of the string or there's a -// whitespace character or an emoji before the emoji. The reason for unicodeRegexp is -// that we need to support inputting multiple emoji with no space between them. -const EMOJI_REGEX = new RegExp('(?:^|\\s|' + UNICODE_REGEX.source + ')(' + EMOTICON_REGEX.source + '|:[+-\\w]*:?)$', 'g'); - -// We also need to match the non-zero-length prefixes to remove them from the final match, -// and update the range so that we don't replace the whitespace or the previous emoji. -const MATCH_PREFIX_REGEX = new RegExp('(\\s|' + UNICODE_REGEX.source + ')'); +// Match for ascii-style ";-)" emoticons or ":wink:" shortcodes provided by emojibase +const EMOJI_REGEX = new RegExp('(' + EMOTICON_REGEX.source + '|:[+-\\w]*:?)$', 'g'); const EMOJI_SHORTNAMES = Object.keys(EmojiData).map((key) => EmojiData[key]).sort( (a, b) => { if (a.category === b.category) { return a.emoji_order - b.emoji_order; } - return CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category); + return a.category - b.category; }, ).map((a, index) => { return { @@ -108,20 +89,18 @@ export default class EmojiProvider extends AutocompleteProvider { const {command, range} = this.getCurrentCommand(query, selection); if (command) { let matchedString = command[0]; - - // Remove prefix of any length (single whitespace or unicode emoji) - const prefixMatch = MATCH_PREFIX_REGEX.exec(matchedString); - if (prefixMatch) { - matchedString = matchedString.slice(prefixMatch[0].length); - range.start += prefixMatch[0].length; - } completions = this.matcher.match(matchedString); // Do second match with shouldMatchWordsOnly in order to match against 'name' completions = completions.concat(this.nameMatcher.match(matchedString)); + console.log("pre-sorted completions", completions); + const sorters = []; - // First, sort by score (Infinity if matchedString not in shortname) + // make sure that emoticons come first + sorters.push((c) => score(matchedString, c.aliases_ascii)); + + // then sort by score (Infinity if matchedString not in shortname) sorters.push((c) => score(matchedString, c.shortname)); // If the matchedString is not empty, sort by length of shortname. Example: // matchedString = ":bookmark" @@ -144,6 +123,8 @@ export default class EmojiProvider extends AutocompleteProvider { range, }; }).slice(0, LIMIT); + + console.log("mapped completions", completions); } return completions; } diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 1f71855fa5..2e933964d4 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -61,7 +61,7 @@ import ReplyThread from "../elements/ReplyThread"; import {ContentHelpers} from 'matrix-js-sdk'; import AccessibleButton from '../elements/AccessibleButton'; -const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX + ')\\s$'); +const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); const TYPING_USER_TIMEOUT = 10000; const TYPING_SERVER_TIMEOUT = 30000; @@ -532,14 +532,14 @@ export default class MessageComposerInput extends React.Component { // Automatic replacement of plaintext emoji to Unicode emoji if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) { // The first matched group includes just the matched plaintext emoji - const emojiMatch = REGEX_EMOJI_WHITESPACE.exec(text.slice(0, currentStartOffset)); - if (emojiMatch) { - const unicodeEmoji = EMOJIBASE.find(e => e.emoticon && e.emoticon.contains(emoijMatch[1])); + const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(text.slice(0, currentStartOffset)); + if (emoticonMatch) { + const unicodeEmoji = EMOJIBASE.find(e => e.emoticon && e.emoticon.contains(emoticonMatch[1])); const range = Range.create({ anchor: { key: editorState.startText.key, - offset: currentStartOffset - emojiMatch[1].length - 1, + offset: currentStartOffset - emoticonMatch[1].length - 1, }, focus: { key: editorState.startText.key, diff --git a/src/stripped-emoji.json b/src/stripped-emoji.json index 16b772ae51..a9debd7f95 100644 --- a/src/stripped-emoji.json +++ b/src/stripped-emoji.json @@ -1 +1 @@ -[{"name":"hundred points symbol","shortname":":100:","category":"symbols","emoji_order":"2119"},{"name":"input symbol for numbers","shortname":":1234:","category":"symbols","emoji_order":"2122"},{"name":"grinning face","shortname":":grinning:","category":"people","emoji_order":"1"},{"name":"grinning face with smiling eyes","shortname":":grin:","category":"people","emoji_order":"2"},{"name":"face with tears of joy","shortname":":joy:","category":"people","emoji_order":"3","aliases_ascii":[":')",":'-)"]},{"name":"rolling on the floor laughing","shortname":":rofl:","category":"people","emoji_order":"4","aliases":[":rolling_on_the_floor_laughing:"]},{"name":"smiling face with open mouth","shortname":":smiley:","category":"people","emoji_order":"5","aliases_ascii":[":D",":-D","=D"]},{"name":"smiling face with open mouth and smiling eyes","shortname":":smile:","category":"people","emoji_order":"6"},{"name":"smiling face with open mouth and cold sweat","shortname":":sweat_smile:","category":"people","emoji_order":"7","aliases_ascii":["':)","':-)","'=)","':D","':-D","'=D"]},{"name":"smiling face with open mouth and tightly-closed eyes","shortname":":laughing:","category":"people","emoji_order":"8","aliases":[":satisfied:"],"aliases_ascii":[">:)",">;)",">:-)",">=)"]},{"name":"winking face","shortname":":wink:","category":"people","emoji_order":"9","aliases_ascii":[";)",";-)","*-)","*)",";-]",";]",";D",";^)"]},{"name":"smiling face with smiling eyes","shortname":":blush:","category":"people","emoji_order":"10"},{"name":"face savouring delicious food","shortname":":yum:","category":"people","emoji_order":"11"},{"name":"smiling face with sunglasses","shortname":":sunglasses:","category":"people","emoji_order":"12","aliases_ascii":["B-)","B)","8)","8-)","B-D","8-D"]},{"name":"smiling face with heart-shaped eyes","shortname":":heart_eyes:","category":"people","emoji_order":"13"},{"name":"face throwing a kiss","shortname":":kissing_heart:","category":"people","emoji_order":"14","aliases_ascii":[":*",":-*","=*",":^*"]},{"name":"kissing face","shortname":":kissing:","category":"people","emoji_order":"15"},{"name":"kissing face with smiling eyes","shortname":":kissing_smiling_eyes:","category":"people","emoji_order":"16"},{"name":"kissing face with closed eyes","shortname":":kissing_closed_eyes:","category":"people","emoji_order":"17"},{"name":"white smiling face","shortname":":relaxed:","category":"people","emoji_order":"18"},{"name":"slightly smiling face","shortname":":slight_smile:","category":"people","emoji_order":"19","aliases":[":slightly_smiling_face:"],"aliases_ascii":[":)",":-)","=]","=)",":]"]},{"name":"hugging face","shortname":":hugging:","category":"people","emoji_order":"20","aliases":[":hugging_face:"]},{"name":"thinking face","shortname":":thinking:","category":"people","emoji_order":"21","aliases":[":thinking_face:"]},{"name":"neutral face","shortname":":neutral_face:","category":"people","emoji_order":"22"},{"name":"expressionless face","shortname":":expressionless:","category":"people","emoji_order":"23","aliases_ascii":["-_-","-__-","-___-"]},{"name":"face without mouth","shortname":":no_mouth:","category":"people","emoji_order":"24","aliases_ascii":[":-X",":X",":-#",":#","=X","=x",":x",":-x","=#"]},{"name":"face with rolling eyes","shortname":":rolling_eyes:","category":"people","emoji_order":"25","aliases":[":face_with_rolling_eyes:"]},{"name":"smirking face","shortname":":smirk:","category":"people","emoji_order":"26"},{"name":"persevering face","shortname":":persevere:","category":"people","emoji_order":"27","aliases_ascii":[">.<"]},{"name":"disappointed but relieved face","shortname":":disappointed_relieved:","category":"people","emoji_order":"28"},{"name":"face with open mouth","shortname":":open_mouth:","category":"people","emoji_order":"29","aliases_ascii":[":-O",":O",":-o",":o","O_O",">:O"]},{"name":"zipper-mouth face","shortname":":zipper_mouth:","category":"people","emoji_order":"30","aliases":[":zipper_mouth_face:"]},{"name":"hushed face","shortname":":hushed:","category":"people","emoji_order":"31"},{"name":"sleepy face","shortname":":sleepy:","category":"people","emoji_order":"32"},{"name":"tired face","shortname":":tired_face:","category":"people","emoji_order":"33"},{"name":"sleeping face","shortname":":sleeping:","category":"people","emoji_order":"34"},{"name":"relieved face","shortname":":relieved:","category":"people","emoji_order":"35"},{"name":"nerd face","shortname":":nerd:","category":"people","emoji_order":"36","aliases":[":nerd_face:"]},{"name":"face with stuck-out tongue","shortname":":stuck_out_tongue:","category":"people","emoji_order":"37","aliases_ascii":[":P",":-P","=P",":-p",":p","=p",":-Þ",":Þ",":þ",":-þ",":-b",":b","d:"]},{"name":"face with stuck-out tongue and winking eye","shortname":":stuck_out_tongue_winking_eye:","category":"people","emoji_order":"38","aliases_ascii":[">:P","X-P","x-p"]},{"name":"face with stuck-out tongue and tightly-closed eyes","shortname":":stuck_out_tongue_closed_eyes:","category":"people","emoji_order":"39"},{"name":"drooling face","shortname":":drooling_face:","category":"people","emoji_order":"40","aliases":[":drool:"]},{"name":"unamused face","shortname":":unamused:","category":"people","emoji_order":"41"},{"name":"face with cold sweat","shortname":":sweat:","category":"people","emoji_order":"42","aliases_ascii":["':(","':-(","'=("]},{"name":"pensive face","shortname":":pensive:","category":"people","emoji_order":"43"},{"name":"confused face","shortname":":confused:","category":"people","emoji_order":"44","aliases_ascii":[">:\\",">:/",":-/",":-.",":/",":\\","=/","=\\",":L","=L"]},{"name":"upside-down face","shortname":":upside_down:","category":"people","emoji_order":"45","aliases":[":upside_down_face:"]},{"name":"money-mouth face","shortname":":money_mouth:","category":"people","emoji_order":"46","aliases":[":money_mouth_face:"]},{"name":"astonished face","shortname":":astonished:","category":"people","emoji_order":"47"},{"name":"white frowning face","shortname":":frowning2:","category":"people","emoji_order":"48","aliases":[":white_frowning_face:"]},{"name":"slightly frowning face","shortname":":slight_frown:","category":"people","emoji_order":"49","aliases":[":slightly_frowning_face:"]},{"name":"confounded face","shortname":":confounded:","category":"people","emoji_order":"50"},{"name":"disappointed face","shortname":":disappointed:","category":"people","emoji_order":"51","aliases_ascii":[">:[",":-(",":(",":-[",":[","=("]},{"name":"worried face","shortname":":worried:","category":"people","emoji_order":"52"},{"name":"face with look of triumph","shortname":":triumph:","category":"people","emoji_order":"53"},{"name":"crying face","shortname":":cry:","category":"people","emoji_order":"54","aliases_ascii":[":'(",":'-(",";(",";-("]},{"name":"loudly crying face","shortname":":sob:","category":"people","emoji_order":"55"},{"name":"frowning face with open mouth","shortname":":frowning:","category":"people","emoji_order":"56"},{"name":"anguished face","shortname":":anguished:","category":"people","emoji_order":"57"},{"name":"fearful face","shortname":":fearful:","category":"people","emoji_order":"58","aliases_ascii":["D:"]},{"name":"weary face","shortname":":weary:","category":"people","emoji_order":"59"},{"name":"grimacing face","shortname":":grimacing:","category":"people","emoji_order":"60"},{"name":"face with open mouth and cold sweat","shortname":":cold_sweat:","category":"people","emoji_order":"61"},{"name":"face screaming in fear","shortname":":scream:","category":"people","emoji_order":"62"},{"name":"flushed face","shortname":":flushed:","category":"people","emoji_order":"63","aliases_ascii":[":$","=$"]},{"name":"dizzy face","shortname":":dizzy_face:","category":"people","emoji_order":"64","aliases_ascii":["#-)","#)","%-)","%)","X)","X-)"]},{"name":"pouting face","shortname":":rage:","category":"people","emoji_order":"65"},{"name":"angry face","shortname":":angry:","category":"people","emoji_order":"66","aliases_ascii":[">:(",">:-(",":@"]},{"name":"smiling face with halo","shortname":":innocent:","category":"people","emoji_order":"67","aliases_ascii":["O:-)","0:-3","0:3","0:-)","0:)","0;^)","O:)","O;-)","O=)","0;-)","O:-3","O:3"]},{"name":"face with cowboy hat","shortname":":cowboy:","category":"people","emoji_order":"68","aliases":[":face_with_cowboy_hat:"]},{"name":"clown face","shortname":":clown:","category":"people","emoji_order":"69","aliases":[":clown_face:"]},{"name":"lying face","shortname":":lying_face:","category":"people","emoji_order":"70","aliases":[":liar:"]},{"name":"face with medical mask","shortname":":mask:","category":"people","emoji_order":"71"},{"name":"face with thermometer","shortname":":thermometer_face:","category":"people","emoji_order":"72","aliases":[":face_with_thermometer:"]},{"name":"face with head-bandage","shortname":":head_bandage:","category":"people","emoji_order":"73","aliases":[":face_with_head_bandage:"]},{"name":"nauseated face","shortname":":nauseated_face:","category":"people","emoji_order":"74","aliases":[":sick:"]},{"name":"sneezing face","shortname":":sneezing_face:","category":"people","emoji_order":"75","aliases":[":sneeze:"]},{"name":"smiling face with horns","shortname":":smiling_imp:","category":"people","emoji_order":"76"},{"name":"imp","shortname":":imp:","category":"people","emoji_order":"77"},{"name":"japanese ogre","shortname":":japanese_ogre:","category":"people","emoji_order":"78"},{"name":"japanese goblin","shortname":":japanese_goblin:","category":"people","emoji_order":"79"},{"name":"skull","shortname":":skull:","category":"people","emoji_order":"80","aliases":[":skeleton:"]},{"name":"skull and crossbones","shortname":":skull_crossbones:","category":"objects","emoji_order":"81","aliases":[":skull_and_crossbones:"]},{"name":"ghost","shortname":":ghost:","category":"people","emoji_order":"82"},{"name":"extraterrestrial alien","shortname":":alien:","category":"people","emoji_order":"83"},{"name":"alien monster","shortname":":space_invader:","category":"activity","emoji_order":"84"},{"name":"robot face","shortname":":robot:","category":"people","emoji_order":"85","aliases":[":robot_face:"]},{"name":"pile of poo","shortname":":poop:","category":"people","emoji_order":"86","aliases":[":shit:",":hankey:",":poo:"]},{"name":"smiling cat face with open mouth","shortname":":smiley_cat:","category":"people","emoji_order":"87"},{"name":"grinning cat face with smiling eyes","shortname":":smile_cat:","category":"people","emoji_order":"88"},{"name":"cat face with tears of joy","shortname":":joy_cat:","category":"people","emoji_order":"89"},{"name":"smiling cat face with heart-shaped eyes","shortname":":heart_eyes_cat:","category":"people","emoji_order":"90"},{"name":"cat face with wry smile","shortname":":smirk_cat:","category":"people","emoji_order":"91"},{"name":"kissing cat face with closed eyes","shortname":":kissing_cat:","category":"people","emoji_order":"92"},{"name":"weary cat face","shortname":":scream_cat:","category":"people","emoji_order":"93"},{"name":"crying cat face","shortname":":crying_cat_face:","category":"people","emoji_order":"94"},{"name":"pouting cat face","shortname":":pouting_cat:","category":"people","emoji_order":"95"},{"name":"see-no-evil monkey","shortname":":see_no_evil:","category":"nature","emoji_order":"96"},{"name":"hear-no-evil monkey","shortname":":hear_no_evil:","category":"nature","emoji_order":"97"},{"name":"speak-no-evil monkey","shortname":":speak_no_evil:","category":"nature","emoji_order":"98"},{"name":"boy","shortname":":boy:","category":"people","emoji_order":"99"},{"name":"boy tone 1","shortname":":boy_tone1:","category":"people","emoji_order":"100"},{"name":"boy tone 2","shortname":":boy_tone2:","category":"people","emoji_order":"101"},{"name":"boy tone 3","shortname":":boy_tone3:","category":"people","emoji_order":"102"},{"name":"boy tone 4","shortname":":boy_tone4:","category":"people","emoji_order":"103"},{"name":"boy tone 5","shortname":":boy_tone5:","category":"people","emoji_order":"104"},{"name":"girl","shortname":":girl:","category":"people","emoji_order":"105"},{"name":"girl tone 1","shortname":":girl_tone1:","category":"people","emoji_order":"106"},{"name":"girl tone 2","shortname":":girl_tone2:","category":"people","emoji_order":"107"},{"name":"girl tone 3","shortname":":girl_tone3:","category":"people","emoji_order":"108"},{"name":"girl tone 4","shortname":":girl_tone4:","category":"people","emoji_order":"109"},{"name":"girl tone 5","shortname":":girl_tone5:","category":"people","emoji_order":"110"},{"name":"man","shortname":":man:","category":"people","emoji_order":"111"},{"name":"man tone 1","shortname":":man_tone1:","category":"people","emoji_order":"112"},{"name":"man tone 2","shortname":":man_tone2:","category":"people","emoji_order":"113"},{"name":"man tone 3","shortname":":man_tone3:","category":"people","emoji_order":"114"},{"name":"man tone 4","shortname":":man_tone4:","category":"people","emoji_order":"115"},{"name":"man tone 5","shortname":":man_tone5:","category":"people","emoji_order":"116"},{"name":"woman","shortname":":woman:","category":"people","emoji_order":"117"},{"name":"woman tone 1","shortname":":woman_tone1:","category":"people","emoji_order":"118"},{"name":"woman tone 2","shortname":":woman_tone2:","category":"people","emoji_order":"119"},{"name":"woman tone 3","shortname":":woman_tone3:","category":"people","emoji_order":"120"},{"name":"woman tone 4","shortname":":woman_tone4:","category":"people","emoji_order":"121"},{"name":"woman tone 5","shortname":":woman_tone5:","category":"people","emoji_order":"122"},{"name":"older man","shortname":":older_man:","category":"people","emoji_order":"123"},{"name":"older man tone 1","shortname":":older_man_tone1:","category":"people","emoji_order":"124"},{"name":"older man tone 2","shortname":":older_man_tone2:","category":"people","emoji_order":"125"},{"name":"older man tone 3","shortname":":older_man_tone3:","category":"people","emoji_order":"126"},{"name":"older man tone 4","shortname":":older_man_tone4:","category":"people","emoji_order":"127"},{"name":"older man tone 5","shortname":":older_man_tone5:","category":"people","emoji_order":"128"},{"name":"older woman","shortname":":older_woman:","category":"people","emoji_order":"129","aliases":[":grandma:"]},{"name":"older woman tone 1","shortname":":older_woman_tone1:","category":"people","emoji_order":"130","aliases":[":grandma_tone1:"]},{"name":"older woman tone 2","shortname":":older_woman_tone2:","category":"people","emoji_order":"131","aliases":[":grandma_tone2:"]},{"name":"older woman tone 3","shortname":":older_woman_tone3:","category":"people","emoji_order":"132","aliases":[":grandma_tone3:"]},{"name":"older woman tone 4","shortname":":older_woman_tone4:","category":"people","emoji_order":"133","aliases":[":grandma_tone4:"]},{"name":"older woman tone 5","shortname":":older_woman_tone5:","category":"people","emoji_order":"134","aliases":[":grandma_tone5:"]},{"name":"baby","shortname":":baby:","category":"people","emoji_order":"135"},{"name":"baby tone 1","shortname":":baby_tone1:","category":"people","emoji_order":"136"},{"name":"baby tone 2","shortname":":baby_tone2:","category":"people","emoji_order":"137"},{"name":"baby tone 3","shortname":":baby_tone3:","category":"people","emoji_order":"138"},{"name":"baby tone 4","shortname":":baby_tone4:","category":"people","emoji_order":"139"},{"name":"baby tone 5","shortname":":baby_tone5:","category":"people","emoji_order":"140"},{"name":"baby angel","shortname":":angel:","category":"people","emoji_order":"141"},{"name":"baby angel tone 1","shortname":":angel_tone1:","category":"people","emoji_order":"142"},{"name":"baby angel tone 2","shortname":":angel_tone2:","category":"people","emoji_order":"143"},{"name":"baby angel tone 3","shortname":":angel_tone3:","category":"people","emoji_order":"144"},{"name":"baby angel tone 4","shortname":":angel_tone4:","category":"people","emoji_order":"145"},{"name":"baby angel tone 5","shortname":":angel_tone5:","category":"people","emoji_order":"146"},{"name":"police officer","shortname":":cop:","category":"people","emoji_order":"339"},{"name":"police officer tone 1","shortname":":cop_tone1:","category":"people","emoji_order":"340"},{"name":"police officer tone 2","shortname":":cop_tone2:","category":"people","emoji_order":"341"},{"name":"police officer tone 3","shortname":":cop_tone3:","category":"people","emoji_order":"342"},{"name":"police officer tone 4","shortname":":cop_tone4:","category":"people","emoji_order":"343"},{"name":"police officer tone 5","shortname":":cop_tone5:","category":"people","emoji_order":"344"},{"name":"sleuth or spy","shortname":":spy:","category":"people","emoji_order":"357","aliases":[":sleuth_or_spy:"]},{"name":"sleuth or spy tone 1","shortname":":spy_tone1:","category":"people","emoji_order":"358","aliases":[":sleuth_or_spy_tone1:"]},{"name":"sleuth or spy tone 2","shortname":":spy_tone2:","category":"people","emoji_order":"359","aliases":[":sleuth_or_spy_tone2:"]},{"name":"sleuth or spy tone 3","shortname":":spy_tone3:","category":"people","emoji_order":"360","aliases":[":sleuth_or_spy_tone3:"]},{"name":"sleuth or spy tone 4","shortname":":spy_tone4:","category":"people","emoji_order":"361","aliases":[":sleuth_or_spy_tone4:"]},{"name":"sleuth or spy tone 5","shortname":":spy_tone5:","category":"people","emoji_order":"362","aliases":[":sleuth_or_spy_tone5:"]},{"name":"guardsman","shortname":":guardsman:","category":"people","emoji_order":"375"},{"name":"guardsman tone 1","shortname":":guardsman_tone1:","category":"people","emoji_order":"376"},{"name":"guardsman tone 2","shortname":":guardsman_tone2:","category":"people","emoji_order":"377"},{"name":"guardsman tone 3","shortname":":guardsman_tone3:","category":"people","emoji_order":"378"},{"name":"guardsman tone 4","shortname":":guardsman_tone4:","category":"people","emoji_order":"379"},{"name":"guardsman tone 5","shortname":":guardsman_tone5:","category":"people","emoji_order":"380"},{"name":"construction worker","shortname":":construction_worker:","category":"people","emoji_order":"393"},{"name":"construction worker tone 1","shortname":":construction_worker_tone1:","category":"people","emoji_order":"394"},{"name":"construction worker tone 2","shortname":":construction_worker_tone2:","category":"people","emoji_order":"395"},{"name":"construction worker tone 3","shortname":":construction_worker_tone3:","category":"people","emoji_order":"396"},{"name":"construction worker tone 4","shortname":":construction_worker_tone4:","category":"people","emoji_order":"397"},{"name":"construction worker tone 5","shortname":":construction_worker_tone5:","category":"people","emoji_order":"398"},{"name":"man with turban","shortname":":man_with_turban:","category":"people","emoji_order":"411"},{"name":"man with turban tone 1","shortname":":man_with_turban_tone1:","category":"people","emoji_order":"412"},{"name":"man with turban tone 2","shortname":":man_with_turban_tone2:","category":"people","emoji_order":"413"},{"name":"man with turban tone 3","shortname":":man_with_turban_tone3:","category":"people","emoji_order":"414"},{"name":"man with turban tone 4","shortname":":man_with_turban_tone4:","category":"people","emoji_order":"415"},{"name":"man with turban tone 5","shortname":":man_with_turban_tone5:","category":"people","emoji_order":"416"},{"name":"person with blond hair","shortname":":person_with_blond_hair:","category":"people","emoji_order":"429"},{"name":"person with blond hair tone 1","shortname":":person_with_blond_hair_tone1:","category":"people","emoji_order":"430"},{"name":"person with blond hair tone 2","shortname":":person_with_blond_hair_tone2:","category":"people","emoji_order":"431"},{"name":"person with blond hair tone 3","shortname":":person_with_blond_hair_tone3:","category":"people","emoji_order":"432"},{"name":"person with blond hair tone 4","shortname":":person_with_blond_hair_tone4:","category":"people","emoji_order":"433"},{"name":"person with blond hair tone 5","shortname":":person_with_blond_hair_tone5:","category":"people","emoji_order":"434"},{"name":"father christmas","shortname":":santa:","category":"people","emoji_order":"447"},{"name":"father christmas tone 1","shortname":":santa_tone1:","category":"people","emoji_order":"448"},{"name":"father christmas tone 2","shortname":":santa_tone2:","category":"people","emoji_order":"449"},{"name":"father christmas tone 3","shortname":":santa_tone3:","category":"people","emoji_order":"450"},{"name":"father christmas tone 4","shortname":":santa_tone4:","category":"people","emoji_order":"451"},{"name":"father christmas tone 5","shortname":":santa_tone5:","category":"people","emoji_order":"452"},{"name":"mother christmas","shortname":":mrs_claus:","category":"people","emoji_order":"453","aliases":[":mother_christmas:"]},{"name":"mother christmas tone 1","shortname":":mrs_claus_tone1:","category":"people","emoji_order":"454","aliases":[":mother_christmas_tone1:"]},{"name":"mother christmas tone 2","shortname":":mrs_claus_tone2:","category":"people","emoji_order":"455","aliases":[":mother_christmas_tone2:"]},{"name":"mother christmas tone 3","shortname":":mrs_claus_tone3:","category":"people","emoji_order":"456","aliases":[":mother_christmas_tone3:"]},{"name":"mother christmas tone 4","shortname":":mrs_claus_tone4:","category":"people","emoji_order":"457","aliases":[":mother_christmas_tone4:"]},{"name":"mother christmas tone 5","shortname":":mrs_claus_tone5:","category":"people","emoji_order":"458","aliases":[":mother_christmas_tone5:"]},{"name":"princess","shortname":":princess:","category":"people","emoji_order":"459"},{"name":"princess tone 1","shortname":":princess_tone1:","category":"people","emoji_order":"460"},{"name":"princess tone 2","shortname":":princess_tone2:","category":"people","emoji_order":"461"},{"name":"princess tone 3","shortname":":princess_tone3:","category":"people","emoji_order":"462"},{"name":"princess tone 4","shortname":":princess_tone4:","category":"people","emoji_order":"463"},{"name":"princess tone 5","shortname":":princess_tone5:","category":"people","emoji_order":"464"},{"name":"prince","shortname":":prince:","category":"people","emoji_order":"465"},{"name":"prince tone 1","shortname":":prince_tone1:","category":"people","emoji_order":"466"},{"name":"prince tone 2","shortname":":prince_tone2:","category":"people","emoji_order":"467"},{"name":"prince tone 3","shortname":":prince_tone3:","category":"people","emoji_order":"468"},{"name":"prince tone 4","shortname":":prince_tone4:","category":"people","emoji_order":"469"},{"name":"prince tone 5","shortname":":prince_tone5:","category":"people","emoji_order":"470"},{"name":"bride with veil","shortname":":bride_with_veil:","category":"people","emoji_order":"471"},{"name":"bride with veil tone 1","shortname":":bride_with_veil_tone1:","category":"people","emoji_order":"472"},{"name":"bride with veil tone 2","shortname":":bride_with_veil_tone2:","category":"people","emoji_order":"473"},{"name":"bride with veil tone 3","shortname":":bride_with_veil_tone3:","category":"people","emoji_order":"474"},{"name":"bride with veil tone 4","shortname":":bride_with_veil_tone4:","category":"people","emoji_order":"475"},{"name":"bride with veil tone 5","shortname":":bride_with_veil_tone5:","category":"people","emoji_order":"476"},{"name":"man in tuxedo","shortname":":man_in_tuxedo:","category":"people","emoji_order":"477"},{"name":"man in tuxedo tone 1","shortname":":man_in_tuxedo_tone1:","category":"people","emoji_order":"478","aliases":[":tuxedo_tone1:"]},{"name":"man in tuxedo tone 2","shortname":":man_in_tuxedo_tone2:","category":"people","emoji_order":"479","aliases":[":tuxedo_tone2:"]},{"name":"man in tuxedo tone 3","shortname":":man_in_tuxedo_tone3:","category":"people","emoji_order":"480","aliases":[":tuxedo_tone3:"]},{"name":"man in tuxedo tone 4","shortname":":man_in_tuxedo_tone4:","category":"people","emoji_order":"481","aliases":[":tuxedo_tone4:"]},{"name":"man in tuxedo tone 5","shortname":":man_in_tuxedo_tone5:","category":"people","emoji_order":"482","aliases":[":tuxedo_tone5:"]},{"name":"pregnant woman","shortname":":pregnant_woman:","category":"people","emoji_order":"483","aliases":[":expecting_woman:"]},{"name":"pregnant woman tone 1","shortname":":pregnant_woman_tone1:","category":"people","emoji_order":"484","aliases":[":expecting_woman_tone1:"]},{"name":"pregnant woman tone 2","shortname":":pregnant_woman_tone2:","category":"people","emoji_order":"485","aliases":[":expecting_woman_tone2:"]},{"name":"pregnant woman tone 3","shortname":":pregnant_woman_tone3:","category":"people","emoji_order":"486","aliases":[":expecting_woman_tone3:"]},{"name":"pregnant woman tone 4","shortname":":pregnant_woman_tone4:","category":"people","emoji_order":"487","aliases":[":expecting_woman_tone4:"]},{"name":"pregnant woman tone 5","shortname":":pregnant_woman_tone5:","category":"people","emoji_order":"488","aliases":[":expecting_woman_tone5:"]},{"name":"man with gua pi mao","shortname":":man_with_gua_pi_mao:","category":"people","emoji_order":"489"},{"name":"man with gua pi mao tone 1","shortname":":man_with_gua_pi_mao_tone1:","category":"people","emoji_order":"490"},{"name":"man with gua pi mao tone 2","shortname":":man_with_gua_pi_mao_tone2:","category":"people","emoji_order":"491"},{"name":"man with gua pi mao tone 3","shortname":":man_with_gua_pi_mao_tone3:","category":"people","emoji_order":"492"},{"name":"man with gua pi mao tone 4","shortname":":man_with_gua_pi_mao_tone4:","category":"people","emoji_order":"493"},{"name":"man with gua pi mao tone 5","shortname":":man_with_gua_pi_mao_tone5:","category":"people","emoji_order":"494"},{"name":"person frowning","shortname":":person_frowning:","category":"people","emoji_order":"495"},{"name":"person frowning tone 1","shortname":":person_frowning_tone1:","category":"people","emoji_order":"496"},{"name":"person frowning tone 2","shortname":":person_frowning_tone2:","category":"people","emoji_order":"497"},{"name":"person frowning tone 3","shortname":":person_frowning_tone3:","category":"people","emoji_order":"498"},{"name":"person frowning tone 4","shortname":":person_frowning_tone4:","category":"people","emoji_order":"499"},{"name":"person frowning tone 5","shortname":":person_frowning_tone5:","category":"people","emoji_order":"500"},{"name":"person with pouting face","shortname":":person_with_pouting_face:","category":"people","emoji_order":"513"},{"name":"person with pouting face tone1","shortname":":person_with_pouting_face_tone1:","category":"people","emoji_order":"514"},{"name":"person with pouting face tone2","shortname":":person_with_pouting_face_tone2:","category":"people","emoji_order":"515"},{"name":"person with pouting face tone3","shortname":":person_with_pouting_face_tone3:","category":"people","emoji_order":"516"},{"name":"person with pouting face tone4","shortname":":person_with_pouting_face_tone4:","category":"people","emoji_order":"517"},{"name":"person with pouting face tone5","shortname":":person_with_pouting_face_tone5:","category":"people","emoji_order":"518"},{"name":"face with no good gesture","shortname":":no_good:","category":"people","emoji_order":"531"},{"name":"face with no good gesture tone 1","shortname":":no_good_tone1:","category":"people","emoji_order":"532"},{"name":"face with no good gesture tone 2","shortname":":no_good_tone2:","category":"people","emoji_order":"533"},{"name":"face with no good gesture tone 3","shortname":":no_good_tone3:","category":"people","emoji_order":"534"},{"name":"face with no good gesture tone 4","shortname":":no_good_tone4:","category":"people","emoji_order":"535"},{"name":"face with no good gesture tone 5","shortname":":no_good_tone5:","category":"people","emoji_order":"536"},{"name":"face with ok gesture","shortname":":ok_woman:","category":"people","emoji_order":"549","aliases_ascii":["*\\0/*","\\0/","*\\O/*","\\O/"]},{"name":"face with ok gesture tone1","shortname":":ok_woman_tone1:","category":"people","emoji_order":"550"},{"name":"face with ok gesture tone2","shortname":":ok_woman_tone2:","category":"people","emoji_order":"551"},{"name":"face with ok gesture tone3","shortname":":ok_woman_tone3:","category":"people","emoji_order":"552"},{"name":"face with ok gesture tone4","shortname":":ok_woman_tone4:","category":"people","emoji_order":"553"},{"name":"face with ok gesture tone5","shortname":":ok_woman_tone5:","category":"people","emoji_order":"554"},{"name":"information desk person","shortname":":information_desk_person:","category":"people","emoji_order":"567"},{"name":"information desk person tone 1","shortname":":information_desk_person_tone1:","category":"people","emoji_order":"568"},{"name":"information desk person tone 2","shortname":":information_desk_person_tone2:","category":"people","emoji_order":"569"},{"name":"information desk person tone 3","shortname":":information_desk_person_tone3:","category":"people","emoji_order":"570"},{"name":"information desk person tone 4","shortname":":information_desk_person_tone4:","category":"people","emoji_order":"571"},{"name":"information desk person tone 5","shortname":":information_desk_person_tone5:","category":"people","emoji_order":"572"},{"name":"happy person raising one hand","shortname":":raising_hand:","category":"people","emoji_order":"585"},{"name":"happy person raising one hand tone1","shortname":":raising_hand_tone1:","category":"people","emoji_order":"586"},{"name":"happy person raising one hand tone2","shortname":":raising_hand_tone2:","category":"people","emoji_order":"587"},{"name":"happy person raising one hand tone3","shortname":":raising_hand_tone3:","category":"people","emoji_order":"588"},{"name":"happy person raising one hand tone4","shortname":":raising_hand_tone4:","category":"people","emoji_order":"589"},{"name":"happy person raising one hand tone5","shortname":":raising_hand_tone5:","category":"people","emoji_order":"590"},{"name":"person bowing deeply","shortname":":bow:","category":"people","emoji_order":"603"},{"name":"person bowing deeply tone 1","shortname":":bow_tone1:","category":"people","emoji_order":"604"},{"name":"person bowing deeply tone 2","shortname":":bow_tone2:","category":"people","emoji_order":"605"},{"name":"person bowing deeply tone 3","shortname":":bow_tone3:","category":"people","emoji_order":"606"},{"name":"person bowing deeply tone 4","shortname":":bow_tone4:","category":"people","emoji_order":"607"},{"name":"person bowing deeply tone 5","shortname":":bow_tone5:","category":"people","emoji_order":"608"},{"name":"face palm","shortname":":face_palm:","category":"people","emoji_order":"621","aliases":[":facepalm:"]},{"name":"face palm tone 1","shortname":":face_palm_tone1:","category":"people","emoji_order":"622","aliases":[":facepalm_tone1:"]},{"name":"face palm tone 2","shortname":":face_palm_tone2:","category":"people","emoji_order":"623","aliases":[":facepalm_tone2:"]},{"name":"face palm tone 3","shortname":":face_palm_tone3:","category":"people","emoji_order":"624","aliases":[":facepalm_tone3:"]},{"name":"face palm tone 4","shortname":":face_palm_tone4:","category":"people","emoji_order":"625","aliases":[":facepalm_tone4:"]},{"name":"face palm tone 5","shortname":":face_palm_tone5:","category":"people","emoji_order":"626","aliases":[":facepalm_tone5:"]},{"name":"shrug","shortname":":shrug:","category":"people","emoji_order":"639"},{"name":"shrug tone 1","shortname":":shrug_tone1:","category":"people","emoji_order":"640"},{"name":"shrug tone 2","shortname":":shrug_tone2:","category":"people","emoji_order":"641"},{"name":"shrug tone 3","shortname":":shrug_tone3:","category":"people","emoji_order":"642"},{"name":"shrug tone 4","shortname":":shrug_tone4:","category":"people","emoji_order":"643"},{"name":"shrug tone 5","shortname":":shrug_tone5:","category":"people","emoji_order":"644"},{"name":"face massage","shortname":":massage:","category":"people","emoji_order":"657"},{"name":"face massage tone 1","shortname":":massage_tone1:","category":"people","emoji_order":"658"},{"name":"face massage tone 2","shortname":":massage_tone2:","category":"people","emoji_order":"659"},{"name":"face massage tone 3","shortname":":massage_tone3:","category":"people","emoji_order":"660"},{"name":"face massage tone 4","shortname":":massage_tone4:","category":"people","emoji_order":"661"},{"name":"face massage tone 5","shortname":":massage_tone5:","category":"people","emoji_order":"662"},{"name":"haircut","shortname":":haircut:","category":"people","emoji_order":"675"},{"name":"haircut tone 1","shortname":":haircut_tone1:","category":"people","emoji_order":"676"},{"name":"haircut tone 2","shortname":":haircut_tone2:","category":"people","emoji_order":"677"},{"name":"haircut tone 3","shortname":":haircut_tone3:","category":"people","emoji_order":"678"},{"name":"haircut tone 4","shortname":":haircut_tone4:","category":"people","emoji_order":"679"},{"name":"haircut tone 5","shortname":":haircut_tone5:","category":"people","emoji_order":"680"},{"name":"pedestrian","shortname":":walking:","category":"people","emoji_order":"693"},{"name":"pedestrian tone 1","shortname":":walking_tone1:","category":"people","emoji_order":"694"},{"name":"pedestrian tone 2","shortname":":walking_tone2:","category":"people","emoji_order":"695"},{"name":"pedestrian tone 3","shortname":":walking_tone3:","category":"people","emoji_order":"696"},{"name":"pedestrian tone 4","shortname":":walking_tone4:","category":"people","emoji_order":"697"},{"name":"pedestrian tone 5","shortname":":walking_tone5:","category":"people","emoji_order":"698"},{"name":"runner","shortname":":runner:","category":"people","emoji_order":"711"},{"name":"runner tone 1","shortname":":runner_tone1:","category":"people","emoji_order":"712"},{"name":"runner tone 2","shortname":":runner_tone2:","category":"people","emoji_order":"713"},{"name":"runner tone 3","shortname":":runner_tone3:","category":"people","emoji_order":"714"},{"name":"runner tone 4","shortname":":runner_tone4:","category":"people","emoji_order":"715"},{"name":"runner tone 5","shortname":":runner_tone5:","category":"people","emoji_order":"716"},{"name":"dancer","shortname":":dancer:","category":"people","emoji_order":"729"},{"name":"dancer tone 1","shortname":":dancer_tone1:","category":"people","emoji_order":"730"},{"name":"dancer tone 2","shortname":":dancer_tone2:","category":"people","emoji_order":"731"},{"name":"dancer tone 3","shortname":":dancer_tone3:","category":"people","emoji_order":"732"},{"name":"dancer tone 4","shortname":":dancer_tone4:","category":"people","emoji_order":"733"},{"name":"dancer tone 5","shortname":":dancer_tone5:","category":"people","emoji_order":"734"},{"name":"man dancing","shortname":":man_dancing:","category":"people","emoji_order":"735","aliases":[":male_dancer:"]},{"name":"man dancing tone 1","shortname":":man_dancing_tone1:","category":"people","emoji_order":"736","aliases":[":male_dancer_tone1:"]},{"name":"man dancing tone 2","shortname":":man_dancing_tone2:","category":"people","emoji_order":"737","aliases":[":male_dancer_tone2:"]},{"name":"man dancing tone 3","shortname":":man_dancing_tone3:","category":"people","emoji_order":"738","aliases":[":male_dancer_tone3:"]},{"name":"man dancing tone 4","shortname":":man_dancing_tone4:","category":"people","emoji_order":"739","aliases":[":male_dancer_tone4:"]},{"name":"man dancing tone 5","shortname":":man_dancing_tone5:","category":"people","emoji_order":"740","aliases":[":male_dancer_tone5:"]},{"name":"woman with bunny ears","shortname":":dancers:","category":"people","emoji_order":"741"},{"name":"man in business suit levitating","shortname":":levitate:","category":"activity","emoji_order":"759","aliases":[":man_in_business_suit_levitating:"]},{"name":"speaking head in silhouette","shortname":":speaking_head:","category":"people","emoji_order":"765","aliases":[":speaking_head_in_silhouette:"]},{"name":"bust in silhouette","shortname":":bust_in_silhouette:","category":"people","emoji_order":"766"},{"name":"busts in silhouette","shortname":":busts_in_silhouette:","category":"people","emoji_order":"767"},{"name":"fencer","shortname":":fencer:","category":"activity","emoji_order":"768","aliases":[":fencing:"]},{"name":"horse racing","shortname":":horse_racing:","category":"activity","emoji_order":"769"},{"name":"horse racing tone 1","shortname":":horse_racing_tone1:","category":"activity","emoji_order":"770"},{"name":"horse racing tone 2","shortname":":horse_racing_tone2:","category":"activity","emoji_order":"771"},{"name":"horse racing tone 3","shortname":":horse_racing_tone3:","category":"activity","emoji_order":"772"},{"name":"horse racing tone 4","shortname":":horse_racing_tone4:","category":"activity","emoji_order":"773"},{"name":"horse racing tone 5","shortname":":horse_racing_tone5:","category":"activity","emoji_order":"774"},{"name":"skier","shortname":":skier:","category":"activity","emoji_order":"775"},{"name":"snowboarder","shortname":":snowboarder:","category":"activity","emoji_order":"776"},{"name":"golfer","shortname":":golfer:","category":"activity","emoji_order":"782"},{"name":"surfer","shortname":":surfer:","category":"activity","emoji_order":"800"},{"name":"surfer tone 1","shortname":":surfer_tone1:","category":"activity","emoji_order":"801"},{"name":"surfer tone 2","shortname":":surfer_tone2:","category":"activity","emoji_order":"802"},{"name":"surfer tone 3","shortname":":surfer_tone3:","category":"activity","emoji_order":"803"},{"name":"surfer tone 4","shortname":":surfer_tone4:","category":"activity","emoji_order":"804"},{"name":"surfer tone 5","shortname":":surfer_tone5:","category":"activity","emoji_order":"805"},{"name":"rowboat","shortname":":rowboat:","category":"activity","emoji_order":"818"},{"name":"rowboat tone 1","shortname":":rowboat_tone1:","category":"activity","emoji_order":"819"},{"name":"rowboat tone 2","shortname":":rowboat_tone2:","category":"activity","emoji_order":"820"},{"name":"rowboat tone 3","shortname":":rowboat_tone3:","category":"activity","emoji_order":"821"},{"name":"rowboat tone 4","shortname":":rowboat_tone4:","category":"activity","emoji_order":"822"},{"name":"rowboat tone 5","shortname":":rowboat_tone5:","category":"activity","emoji_order":"823"},{"name":"swimmer","shortname":":swimmer:","category":"activity","emoji_order":"836"},{"name":"swimmer tone 1","shortname":":swimmer_tone1:","category":"activity","emoji_order":"837"},{"name":"swimmer tone 2","shortname":":swimmer_tone2:","category":"activity","emoji_order":"838"},{"name":"swimmer tone 3","shortname":":swimmer_tone3:","category":"activity","emoji_order":"839"},{"name":"swimmer tone 4","shortname":":swimmer_tone4:","category":"activity","emoji_order":"840"},{"name":"swimmer tone 5","shortname":":swimmer_tone5:","category":"activity","emoji_order":"841"},{"name":"person with ball","shortname":":basketball_player:","category":"activity","emoji_order":"854","aliases":[":person_with_ball:"]},{"name":"person with ball tone 1","shortname":":basketball_player_tone1:","category":"activity","emoji_order":"855","aliases":[":person_with_ball_tone1:"]},{"name":"person with ball tone 2","shortname":":basketball_player_tone2:","category":"activity","emoji_order":"856","aliases":[":person_with_ball_tone2:"]},{"name":"person with ball tone 3","shortname":":basketball_player_tone3:","category":"activity","emoji_order":"857","aliases":[":person_with_ball_tone3:"]},{"name":"person with ball tone 4","shortname":":basketball_player_tone4:","category":"activity","emoji_order":"858","aliases":[":person_with_ball_tone4:"]},{"name":"person with ball tone 5","shortname":":basketball_player_tone5:","category":"activity","emoji_order":"859","aliases":[":person_with_ball_tone5:"]},{"name":"weight lifter","shortname":":lifter:","category":"activity","emoji_order":"872","aliases":[":weight_lifter:"]},{"name":"weight lifter tone 1","shortname":":lifter_tone1:","category":"activity","emoji_order":"873","aliases":[":weight_lifter_tone1:"]},{"name":"weight lifter tone 2","shortname":":lifter_tone2:","category":"activity","emoji_order":"874","aliases":[":weight_lifter_tone2:"]},{"name":"weight lifter tone 3","shortname":":lifter_tone3:","category":"activity","emoji_order":"875","aliases":[":weight_lifter_tone3:"]},{"name":"weight lifter tone 4","shortname":":lifter_tone4:","category":"activity","emoji_order":"876","aliases":[":weight_lifter_tone4:"]},{"name":"weight lifter tone 5","shortname":":lifter_tone5:","category":"activity","emoji_order":"877","aliases":[":weight_lifter_tone5:"]},{"name":"bicyclist","shortname":":bicyclist:","category":"activity","emoji_order":"890"},{"name":"bicyclist tone 1","shortname":":bicyclist_tone1:","category":"activity","emoji_order":"891"},{"name":"bicyclist tone 2","shortname":":bicyclist_tone2:","category":"activity","emoji_order":"892"},{"name":"bicyclist tone 3","shortname":":bicyclist_tone3:","category":"activity","emoji_order":"893"},{"name":"bicyclist tone 4","shortname":":bicyclist_tone4:","category":"activity","emoji_order":"894"},{"name":"bicyclist tone 5","shortname":":bicyclist_tone5:","category":"activity","emoji_order":"895"},{"name":"mountain bicyclist","shortname":":mountain_bicyclist:","category":"activity","emoji_order":"908"},{"name":"mountain bicyclist tone 1","shortname":":mountain_bicyclist_tone1:","category":"activity","emoji_order":"909"},{"name":"mountain bicyclist tone 2","shortname":":mountain_bicyclist_tone2:","category":"activity","emoji_order":"910"},{"name":"mountain bicyclist tone 3","shortname":":mountain_bicyclist_tone3:","category":"activity","emoji_order":"911"},{"name":"mountain bicyclist tone 4","shortname":":mountain_bicyclist_tone4:","category":"activity","emoji_order":"912"},{"name":"mountain bicyclist tone 5","shortname":":mountain_bicyclist_tone5:","category":"activity","emoji_order":"913"},{"name":"racing car","shortname":":race_car:","category":"travel","emoji_order":"926","aliases":[":racing_car:"]},{"name":"racing motorcycle","shortname":":motorcycle:","category":"travel","emoji_order":"927","aliases":[":racing_motorcycle:"]},{"name":"person doing cartwheel","shortname":":cartwheel:","category":"activity","emoji_order":"928","aliases":[":person_doing_cartwheel:"]},{"name":"person doing cartwheel tone 1","shortname":":cartwheel_tone1:","category":"activity","emoji_order":"929","aliases":[":person_doing_cartwheel_tone1:"]},{"name":"person doing cartwheel tone 2","shortname":":cartwheel_tone2:","category":"activity","emoji_order":"930","aliases":[":person_doing_cartwheel_tone2:"]},{"name":"person doing cartwheel tone 3","shortname":":cartwheel_tone3:","category":"activity","emoji_order":"931","aliases":[":person_doing_cartwheel_tone3:"]},{"name":"person doing cartwheel tone 4","shortname":":cartwheel_tone4:","category":"activity","emoji_order":"932","aliases":[":person_doing_cartwheel_tone4:"]},{"name":"person doing cartwheel tone 5","shortname":":cartwheel_tone5:","category":"activity","emoji_order":"933","aliases":[":person_doing_cartwheel_tone5:"]},{"name":"wrestlers","shortname":":wrestlers:","category":"activity","emoji_order":"946","aliases":[":wrestling:"]},{"name":"wrestlers tone 1","shortname":":wrestlers_tone1:","category":"activity","emoji_order":"947","aliases":[":wrestling_tone1:"]},{"name":"wrestlers tone 2","shortname":":wrestlers_tone2:","category":"activity","emoji_order":"948","aliases":[":wrestling_tone2:"]},{"name":"wrestlers tone 3","shortname":":wrestlers_tone3:","category":"activity","emoji_order":"949","aliases":[":wrestling_tone3:"]},{"name":"wrestlers tone 4","shortname":":wrestlers_tone4:","category":"activity","emoji_order":"950","aliases":[":wrestling_tone4:"]},{"name":"wrestlers tone 5","shortname":":wrestlers_tone5:","category":"activity","emoji_order":"951","aliases":[":wrestling_tone5:"]},{"name":"water polo","shortname":":water_polo:","category":"activity","emoji_order":"964"},{"name":"water polo tone 1","shortname":":water_polo_tone1:","category":"activity","emoji_order":"965"},{"name":"water polo tone 2","shortname":":water_polo_tone2:","category":"activity","emoji_order":"966"},{"name":"water polo tone 3","shortname":":water_polo_tone3:","category":"activity","emoji_order":"967"},{"name":"water polo tone 4","shortname":":water_polo_tone4:","category":"activity","emoji_order":"968"},{"name":"water polo tone 5","shortname":":water_polo_tone5:","category":"activity","emoji_order":"969"},{"name":"handball","shortname":":handball:","category":"activity","emoji_order":"982"},{"name":"handball tone 1","shortname":":handball_tone1:","category":"activity","emoji_order":"983"},{"name":"handball tone 2","shortname":":handball_tone2:","category":"activity","emoji_order":"984"},{"name":"handball tone 3","shortname":":handball_tone3:","category":"activity","emoji_order":"985"},{"name":"handball tone 4","shortname":":handball_tone4:","category":"activity","emoji_order":"986"},{"name":"handball tone 5","shortname":":handball_tone5:","category":"activity","emoji_order":"987"},{"name":"juggling","shortname":":juggling:","category":"activity","emoji_order":"1000","aliases":[":juggler:"]},{"name":"juggling tone 1","shortname":":juggling_tone1:","category":"activity","emoji_order":"1001","aliases":[":juggler_tone1:"]},{"name":"juggling tone 2","shortname":":juggling_tone2:","category":"activity","emoji_order":"1002","aliases":[":juggler_tone2:"]},{"name":"juggling tone 3","shortname":":juggling_tone3:","category":"activity","emoji_order":"1003","aliases":[":juggler_tone3:"]},{"name":"juggling tone 4","shortname":":juggling_tone4:","category":"activity","emoji_order":"1004","aliases":[":juggler_tone4:"]},{"name":"juggling tone 5","shortname":":juggling_tone5:","category":"activity","emoji_order":"1005","aliases":[":juggler_tone5:"]},{"name":"man and woman holding hands","shortname":":couple:","category":"people","emoji_order":"1018"},{"name":"two men holding hands","shortname":":two_men_holding_hands:","category":"people","emoji_order":"1024"},{"name":"two women holding hands","shortname":":two_women_holding_hands:","category":"people","emoji_order":"1030"},{"name":"kiss","shortname":":couplekiss:","category":"people","emoji_order":"1036"},{"name":"kiss (man,man)","shortname":":kiss_mm:","category":"people","emoji_order":"1038","aliases":[":couplekiss_mm:"]},{"name":"kiss (woman,woman)","shortname":":kiss_ww:","category":"people","emoji_order":"1039","aliases":[":couplekiss_ww:"]},{"name":"couple with heart","shortname":":couple_with_heart:","category":"people","emoji_order":"1040"},{"name":"couple (man,man)","shortname":":couple_mm:","category":"people","emoji_order":"1042","aliases":[":couple_with_heart_mm:"]},{"name":"couple (woman,woman)","shortname":":couple_ww:","category":"people","emoji_order":"1043","aliases":[":couple_with_heart_ww:"]},{"name":"family","shortname":":family:","category":"people","emoji_order":"1044"},{"name":"family (man,woman,girl)","shortname":":family_mwg:","category":"people","emoji_order":"1051"},{"name":"family (man,woman,girl,boy)","shortname":":family_mwgb:","category":"people","emoji_order":"1052"},{"name":"family (man,woman,boy,boy)","shortname":":family_mwbb:","category":"people","emoji_order":"1053"},{"name":"family (man,woman,girl,girl)","shortname":":family_mwgg:","category":"people","emoji_order":"1054"},{"name":"family (man,man,boy)","shortname":":family_mmb:","category":"people","emoji_order":"1055"},{"name":"family (man,man,girl)","shortname":":family_mmg:","category":"people","emoji_order":"1056"},{"name":"family (man,man,girl,boy)","shortname":":family_mmgb:","category":"people","emoji_order":"1057"},{"name":"family (man,man,boy,boy)","shortname":":family_mmbb:","category":"people","emoji_order":"1058"},{"name":"family (man,man,girl,girl)","shortname":":family_mmgg:","category":"people","emoji_order":"1059"},{"name":"family (woman,woman,boy)","shortname":":family_wwb:","category":"people","emoji_order":"1060"},{"name":"family (woman,woman,girl)","shortname":":family_wwg:","category":"people","emoji_order":"1061"},{"name":"family (woman,woman,girl,boy)","shortname":":family_wwgb:","category":"people","emoji_order":"1062"},{"name":"family (woman,woman,boy,boy)","shortname":":family_wwbb:","category":"people","emoji_order":"1063"},{"name":"family (woman,woman,girl,girl)","shortname":":family_wwgg:","category":"people","emoji_order":"1064"},{"name":"emoji modifier Fitzpatrick type-1-2","shortname":":tone1:","category":"modifier","emoji_order":"1075"},{"name":"emoji modifier Fitzpatrick type-3","shortname":":tone2:","category":"modifier","emoji_order":"1076"},{"name":"emoji modifier Fitzpatrick type-4","shortname":":tone3:","category":"modifier","emoji_order":"1077"},{"name":"emoji modifier Fitzpatrick type-5","shortname":":tone4:","category":"modifier","emoji_order":"1078"},{"name":"emoji modifier Fitzpatrick type-6","shortname":":tone5:","category":"modifier","emoji_order":"1079"},{"name":"flexed biceps","shortname":":muscle:","category":"people","emoji_order":"1080"},{"name":"flexed biceps tone 1","shortname":":muscle_tone1:","category":"people","emoji_order":"1081"},{"name":"flexed biceps tone 2","shortname":":muscle_tone2:","category":"people","emoji_order":"1082"},{"name":"flexed biceps tone 3","shortname":":muscle_tone3:","category":"people","emoji_order":"1083"},{"name":"flexed biceps tone 4","shortname":":muscle_tone4:","category":"people","emoji_order":"1084"},{"name":"flexed biceps tone 5","shortname":":muscle_tone5:","category":"people","emoji_order":"1085"},{"name":"selfie","shortname":":selfie:","category":"people","emoji_order":"1086"},{"name":"selfie tone 1","shortname":":selfie_tone1:","category":"people","emoji_order":"1087"},{"name":"selfie tone 2","shortname":":selfie_tone2:","category":"people","emoji_order":"1088"},{"name":"selfie tone 3","shortname":":selfie_tone3:","category":"people","emoji_order":"1089"},{"name":"selfie tone 4","shortname":":selfie_tone4:","category":"people","emoji_order":"1090"},{"name":"selfie tone 5","shortname":":selfie_tone5:","category":"people","emoji_order":"1091"},{"name":"white left pointing backhand index","shortname":":point_left:","category":"people","emoji_order":"1092"},{"name":"white left pointing backhand index tone 1","shortname":":point_left_tone1:","category":"people","emoji_order":"1093"},{"name":"white left pointing backhand index tone 2","shortname":":point_left_tone2:","category":"people","emoji_order":"1094"},{"name":"white left pointing backhand index tone 3","shortname":":point_left_tone3:","category":"people","emoji_order":"1095"},{"name":"white left pointing backhand index tone 4","shortname":":point_left_tone4:","category":"people","emoji_order":"1096"},{"name":"white left pointing backhand index tone 5","shortname":":point_left_tone5:","category":"people","emoji_order":"1097"},{"name":"white right pointing backhand index","shortname":":point_right:","category":"people","emoji_order":"1098"},{"name":"white right pointing backhand index tone 1","shortname":":point_right_tone1:","category":"people","emoji_order":"1099"},{"name":"white right pointing backhand index tone 2","shortname":":point_right_tone2:","category":"people","emoji_order":"1100"},{"name":"white right pointing backhand index tone 3","shortname":":point_right_tone3:","category":"people","emoji_order":"1101"},{"name":"white right pointing backhand index tone 4","shortname":":point_right_tone4:","category":"people","emoji_order":"1102"},{"name":"white right pointing backhand index tone 5","shortname":":point_right_tone5:","category":"people","emoji_order":"1103"},{"name":"white up pointing index","shortname":":point_up:","category":"people","emoji_order":"1104"},{"name":"white up pointing index tone 1","shortname":":point_up_tone1:","category":"people","emoji_order":"1105"},{"name":"white up pointing index tone 2","shortname":":point_up_tone2:","category":"people","emoji_order":"1106"},{"name":"white up pointing index tone 3","shortname":":point_up_tone3:","category":"people","emoji_order":"1107"},{"name":"white up pointing index tone 4","shortname":":point_up_tone4:","category":"people","emoji_order":"1108"},{"name":"white up pointing index tone 5","shortname":":point_up_tone5:","category":"people","emoji_order":"1109"},{"name":"white up pointing backhand index","shortname":":point_up_2:","category":"people","emoji_order":"1110"},{"name":"white up pointing backhand index tone 1","shortname":":point_up_2_tone1:","category":"people","emoji_order":"1111"},{"name":"white up pointing backhand index tone 2","shortname":":point_up_2_tone2:","category":"people","emoji_order":"1112"},{"name":"white up pointing backhand index tone 3","shortname":":point_up_2_tone3:","category":"people","emoji_order":"1113"},{"name":"white up pointing backhand index tone 4","shortname":":point_up_2_tone4:","category":"people","emoji_order":"1114"},{"name":"white up pointing backhand index tone 5","shortname":":point_up_2_tone5:","category":"people","emoji_order":"1115"},{"name":"reversed hand with middle finger extended","shortname":":middle_finger:","category":"people","emoji_order":"1116","aliases":[":reversed_hand_with_middle_finger_extended:"]},{"name":"reversed hand with middle finger extended tone 1","shortname":":middle_finger_tone1:","category":"people","emoji_order":"1117","aliases":[":reversed_hand_with_middle_finger_extended_tone1:"]},{"name":"reversed hand with middle finger extended tone 2","shortname":":middle_finger_tone2:","category":"people","emoji_order":"1118","aliases":[":reversed_hand_with_middle_finger_extended_tone2:"]},{"name":"reversed hand with middle finger extended tone 3","shortname":":middle_finger_tone3:","category":"people","emoji_order":"1119","aliases":[":reversed_hand_with_middle_finger_extended_tone3:"]},{"name":"reversed hand with middle finger extended tone 4","shortname":":middle_finger_tone4:","category":"people","emoji_order":"1120","aliases":[":reversed_hand_with_middle_finger_extended_tone4:"]},{"name":"reversed hand with middle finger extended tone 5","shortname":":middle_finger_tone5:","category":"people","emoji_order":"1121","aliases":[":reversed_hand_with_middle_finger_extended_tone5:"]},{"name":"white down pointing backhand index","shortname":":point_down:","category":"people","emoji_order":"1122"},{"name":"white down pointing backhand index tone 1","shortname":":point_down_tone1:","category":"people","emoji_order":"1123"},{"name":"white down pointing backhand index tone 2","shortname":":point_down_tone2:","category":"people","emoji_order":"1124"},{"name":"white down pointing backhand index tone 3","shortname":":point_down_tone3:","category":"people","emoji_order":"1125"},{"name":"white down pointing backhand index tone 4","shortname":":point_down_tone4:","category":"people","emoji_order":"1126"},{"name":"white down pointing backhand index tone 5","shortname":":point_down_tone5:","category":"people","emoji_order":"1127"},{"name":"victory hand","shortname":":v:","category":"people","emoji_order":"1128"},{"name":"victory hand tone 1","shortname":":v_tone1:","category":"people","emoji_order":"1129"},{"name":"victory hand tone 2","shortname":":v_tone2:","category":"people","emoji_order":"1130"},{"name":"victory hand tone 3","shortname":":v_tone3:","category":"people","emoji_order":"1131"},{"name":"victory hand tone 4","shortname":":v_tone4:","category":"people","emoji_order":"1132"},{"name":"victory hand tone 5","shortname":":v_tone5:","category":"people","emoji_order":"1133"},{"name":"hand with first and index finger crossed","shortname":":fingers_crossed:","category":"people","emoji_order":"1134","aliases":[":hand_with_index_and_middle_finger_crossed:"]},{"name":"hand with index and middle fingers crossed tone 1","shortname":":fingers_crossed_tone1:","category":"people","emoji_order":"1135","aliases":[":hand_with_index_and_middle_fingers_crossed_tone1:"]},{"name":"hand with index and middle fingers crossed tone 2","shortname":":fingers_crossed_tone2:","category":"people","emoji_order":"1136","aliases":[":hand_with_index_and_middle_fingers_crossed_tone2:"]},{"name":"hand with index and middle fingers crossed tone 3","shortname":":fingers_crossed_tone3:","category":"people","emoji_order":"1137","aliases":[":hand_with_index_and_middle_fingers_crossed_tone3:"]},{"name":"hand with index and middle fingers crossed tone 4","shortname":":fingers_crossed_tone4:","category":"people","emoji_order":"1138","aliases":[":hand_with_index_and_middle_fingers_crossed_tone4:"]},{"name":"hand with index and middle fingers crossed tone 5","shortname":":fingers_crossed_tone5:","category":"people","emoji_order":"1139","aliases":[":hand_with_index_and_middle_fingers_crossed_tone5:"]},{"name":"raised hand with part between middle and ring fingers","shortname":":vulcan:","category":"people","emoji_order":"1140","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers:"]},{"name":"raised hand with part between middle and ring fingers tone 1","shortname":":vulcan_tone1:","category":"people","emoji_order":"1141","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers_tone1:"]},{"name":"raised hand with part between middle and ring fingers tone 2","shortname":":vulcan_tone2:","category":"people","emoji_order":"1142","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers_tone2:"]},{"name":"raised hand with part between middle and ring fingers tone 3","shortname":":vulcan_tone3:","category":"people","emoji_order":"1143","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers_tone3:"]},{"name":"raised hand with part between middle and ring fingers tone 4","shortname":":vulcan_tone4:","category":"people","emoji_order":"1144","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers_tone4:"]},{"name":"raised hand with part between middle and ring fingers tone 5","shortname":":vulcan_tone5:","category":"people","emoji_order":"1145","aliases":[":raised_hand_with_part_between_middle_and_ring_fingers_tone5:"]},{"name":"sign of the horns","shortname":":metal:","category":"people","emoji_order":"1146","aliases":[":sign_of_the_horns:"]},{"name":"sign of the horns tone 1","shortname":":metal_tone1:","category":"people","emoji_order":"1147","aliases":[":sign_of_the_horns_tone1:"]},{"name":"sign of the horns tone 2","shortname":":metal_tone2:","category":"people","emoji_order":"1148","aliases":[":sign_of_the_horns_tone2:"]},{"name":"sign of the horns tone 3","shortname":":metal_tone3:","category":"people","emoji_order":"1149","aliases":[":sign_of_the_horns_tone3:"]},{"name":"sign of the horns tone 4","shortname":":metal_tone4:","category":"people","emoji_order":"1150","aliases":[":sign_of_the_horns_tone4:"]},{"name":"sign of the horns tone 5","shortname":":metal_tone5:","category":"people","emoji_order":"1151","aliases":[":sign_of_the_horns_tone5:"]},{"name":"call me hand","shortname":":call_me:","category":"people","emoji_order":"1152","aliases":[":call_me_hand:"]},{"name":"call me hand tone 1","shortname":":call_me_tone1:","category":"people","emoji_order":"1153","aliases":[":call_me_hand_tone1:"]},{"name":"call me hand tone 2","shortname":":call_me_tone2:","category":"people","emoji_order":"1154","aliases":[":call_me_hand_tone2:"]},{"name":"call me hand tone 3","shortname":":call_me_tone3:","category":"people","emoji_order":"1155","aliases":[":call_me_hand_tone3:"]},{"name":"call me hand tone 4","shortname":":call_me_tone4:","category":"people","emoji_order":"1156","aliases":[":call_me_hand_tone4:"]},{"name":"call me hand tone 5","shortname":":call_me_tone5:","category":"people","emoji_order":"1157","aliases":[":call_me_hand_tone5:"]},{"name":"raised hand with fingers splayed","shortname":":hand_splayed:","category":"people","emoji_order":"1158","aliases":[":raised_hand_with_fingers_splayed:"]},{"name":"raised hand with fingers splayed tone 1","shortname":":hand_splayed_tone1:","category":"people","emoji_order":"1159","aliases":[":raised_hand_with_fingers_splayed_tone1:"]},{"name":"raised hand with fingers splayed tone 2","shortname":":hand_splayed_tone2:","category":"people","emoji_order":"1160","aliases":[":raised_hand_with_fingers_splayed_tone2:"]},{"name":"raised hand with fingers splayed tone 3","shortname":":hand_splayed_tone3:","category":"people","emoji_order":"1161","aliases":[":raised_hand_with_fingers_splayed_tone3:"]},{"name":"raised hand with fingers splayed tone 4","shortname":":hand_splayed_tone4:","category":"people","emoji_order":"1162","aliases":[":raised_hand_with_fingers_splayed_tone4:"]},{"name":"raised hand with fingers splayed tone 5","shortname":":hand_splayed_tone5:","category":"people","emoji_order":"1163","aliases":[":raised_hand_with_fingers_splayed_tone5:"]},{"name":"raised hand","shortname":":raised_hand:","category":"people","emoji_order":"1164"},{"name":"raised hand tone 1","shortname":":raised_hand_tone1:","category":"people","emoji_order":"1165"},{"name":"raised hand tone 2","shortname":":raised_hand_tone2:","category":"people","emoji_order":"1166"},{"name":"raised hand tone 3","shortname":":raised_hand_tone3:","category":"people","emoji_order":"1167"},{"name":"raised hand tone 4","shortname":":raised_hand_tone4:","category":"people","emoji_order":"1168"},{"name":"raised hand tone 5","shortname":":raised_hand_tone5:","category":"people","emoji_order":"1169"},{"name":"ok hand sign","shortname":":ok_hand:","category":"people","emoji_order":"1170"},{"name":"ok hand sign tone 1","shortname":":ok_hand_tone1:","category":"people","emoji_order":"1171"},{"name":"ok hand sign tone 2","shortname":":ok_hand_tone2:","category":"people","emoji_order":"1172"},{"name":"ok hand sign tone 3","shortname":":ok_hand_tone3:","category":"people","emoji_order":"1173"},{"name":"ok hand sign tone 4","shortname":":ok_hand_tone4:","category":"people","emoji_order":"1174"},{"name":"ok hand sign tone 5","shortname":":ok_hand_tone5:","category":"people","emoji_order":"1175"},{"name":"thumbs up sign","shortname":":thumbsup:","category":"people","emoji_order":"1176","aliases":[":+1:",":thumbup:"]},{"name":"thumbs up sign tone 1","shortname":":thumbsup_tone1:","category":"people","emoji_order":"1177","aliases":[":+1_tone1:",":thumbup_tone1:"]},{"name":"thumbs up sign tone 2","shortname":":thumbsup_tone2:","category":"people","emoji_order":"1178","aliases":[":+1_tone2:",":thumbup_tone2:"]},{"name":"thumbs up sign tone 3","shortname":":thumbsup_tone3:","category":"people","emoji_order":"1179","aliases":[":+1_tone3:",":thumbup_tone3:"]},{"name":"thumbs up sign tone 4","shortname":":thumbsup_tone4:","category":"people","emoji_order":"1180","aliases":[":+1_tone4:",":thumbup_tone4:"]},{"name":"thumbs up sign tone 5","shortname":":thumbsup_tone5:","category":"people","emoji_order":"1181","aliases":[":+1_tone5:",":thumbup_tone5:"]},{"name":"thumbs down sign","shortname":":thumbsdown:","category":"people","emoji_order":"1182","aliases":[":-1:",":thumbdown:"]},{"name":"thumbs down sign tone 1","shortname":":thumbsdown_tone1:","category":"people","emoji_order":"1183","aliases":[":-1_tone1:",":thumbdown_tone1:"]},{"name":"thumbs down sign tone 2","shortname":":thumbsdown_tone2:","category":"people","emoji_order":"1184","aliases":[":-1_tone2:",":thumbdown_tone2:"]},{"name":"thumbs down sign tone 3","shortname":":thumbsdown_tone3:","category":"people","emoji_order":"1185","aliases":[":-1_tone3:",":thumbdown_tone3:"]},{"name":"thumbs down sign tone 4","shortname":":thumbsdown_tone4:","category":"people","emoji_order":"1186","aliases":[":-1_tone4:",":thumbdown_tone4:"]},{"name":"thumbs down sign tone 5","shortname":":thumbsdown_tone5:","category":"people","emoji_order":"1187","aliases":[":-1_tone5:",":thumbdown_tone5:"]},{"name":"raised fist","shortname":":fist:","category":"people","emoji_order":"1188"},{"name":"raised fist tone 1","shortname":":fist_tone1:","category":"people","emoji_order":"1189"},{"name":"raised fist tone 2","shortname":":fist_tone2:","category":"people","emoji_order":"1190"},{"name":"raised fist tone 3","shortname":":fist_tone3:","category":"people","emoji_order":"1191"},{"name":"raised fist tone 4","shortname":":fist_tone4:","category":"people","emoji_order":"1192"},{"name":"raised fist tone 5","shortname":":fist_tone5:","category":"people","emoji_order":"1193"},{"name":"fisted hand sign","shortname":":punch:","category":"people","emoji_order":"1194"},{"name":"fisted hand sign tone 1","shortname":":punch_tone1:","category":"people","emoji_order":"1195"},{"name":"fisted hand sign tone 2","shortname":":punch_tone2:","category":"people","emoji_order":"1196"},{"name":"fisted hand sign tone 3","shortname":":punch_tone3:","category":"people","emoji_order":"1197"},{"name":"fisted hand sign tone 4","shortname":":punch_tone4:","category":"people","emoji_order":"1198"},{"name":"fisted hand sign tone 5","shortname":":punch_tone5:","category":"people","emoji_order":"1199"},{"name":"left-facing fist","shortname":":left_facing_fist:","category":"people","emoji_order":"1200","aliases":[":left_fist:"]},{"name":"left facing fist tone 1","shortname":":left_facing_fist_tone1:","category":"people","emoji_order":"1201","aliases":[":left_fist_tone1:"]},{"name":"left facing fist tone 2","shortname":":left_facing_fist_tone2:","category":"people","emoji_order":"1202","aliases":[":left_fist_tone2:"]},{"name":"left facing fist tone 3","shortname":":left_facing_fist_tone3:","category":"people","emoji_order":"1203","aliases":[":left_fist_tone3:"]},{"name":"left facing fist tone 4","shortname":":left_facing_fist_tone4:","category":"people","emoji_order":"1204","aliases":[":left_fist_tone4:"]},{"name":"left facing fist tone 5","shortname":":left_facing_fist_tone5:","category":"people","emoji_order":"1205","aliases":[":left_fist_tone5:"]},{"name":"right-facing fist","shortname":":right_facing_fist:","category":"people","emoji_order":"1206","aliases":[":right_fist:"]},{"name":"right facing fist tone 1","shortname":":right_facing_fist_tone1:","category":"people","emoji_order":"1207","aliases":[":right_fist_tone1:"]},{"name":"right facing fist tone 2","shortname":":right_facing_fist_tone2:","category":"people","emoji_order":"1208","aliases":[":right_fist_tone2:"]},{"name":"right facing fist tone 3","shortname":":right_facing_fist_tone3:","category":"people","emoji_order":"1209","aliases":[":right_fist_tone3:"]},{"name":"right facing fist tone 4","shortname":":right_facing_fist_tone4:","category":"people","emoji_order":"1210","aliases":[":right_fist_tone4:"]},{"name":"right facing fist tone 5","shortname":":right_facing_fist_tone5:","category":"people","emoji_order":"1211","aliases":[":right_fist_tone5:"]},{"name":"raised back of hand","shortname":":raised_back_of_hand:","category":"people","emoji_order":"1212","aliases":[":back_of_hand:"]},{"name":"raised back of hand tone 1","shortname":":raised_back_of_hand_tone1:","category":"people","emoji_order":"1213","aliases":[":back_of_hand_tone1:"]},{"name":"raised back of hand tone 2","shortname":":raised_back_of_hand_tone2:","category":"people","emoji_order":"1214","aliases":[":back_of_hand_tone2:"]},{"name":"raised back of hand tone 3","shortname":":raised_back_of_hand_tone3:","category":"people","emoji_order":"1215","aliases":[":back_of_hand_tone3:"]},{"name":"raised back of hand tone 4","shortname":":raised_back_of_hand_tone4:","category":"people","emoji_order":"1216","aliases":[":back_of_hand_tone4:"]},{"name":"raised back of hand tone 5","shortname":":raised_back_of_hand_tone5:","category":"people","emoji_order":"1217","aliases":[":back_of_hand_tone5:"]},{"name":"waving hand sign","shortname":":wave:","category":"people","emoji_order":"1218"},{"name":"waving hand sign tone 1","shortname":":wave_tone1:","category":"people","emoji_order":"1219"},{"name":"waving hand sign tone 2","shortname":":wave_tone2:","category":"people","emoji_order":"1220"},{"name":"waving hand sign tone 3","shortname":":wave_tone3:","category":"people","emoji_order":"1221"},{"name":"waving hand sign tone 4","shortname":":wave_tone4:","category":"people","emoji_order":"1222"},{"name":"waving hand sign tone 5","shortname":":wave_tone5:","category":"people","emoji_order":"1223"},{"name":"clapping hands sign","shortname":":clap:","category":"people","emoji_order":"1224"},{"name":"clapping hands sign tone 1","shortname":":clap_tone1:","category":"people","emoji_order":"1225"},{"name":"clapping hands sign tone 2","shortname":":clap_tone2:","category":"people","emoji_order":"1226"},{"name":"clapping hands sign tone 3","shortname":":clap_tone3:","category":"people","emoji_order":"1227"},{"name":"clapping hands sign tone 4","shortname":":clap_tone4:","category":"people","emoji_order":"1228"},{"name":"clapping hands sign tone 5","shortname":":clap_tone5:","category":"people","emoji_order":"1229"},{"name":"writing hand","shortname":":writing_hand:","category":"people","emoji_order":"1230"},{"name":"writing hand tone 1","shortname":":writing_hand_tone1:","category":"people","emoji_order":"1231"},{"name":"writing hand tone 2","shortname":":writing_hand_tone2:","category":"people","emoji_order":"1232"},{"name":"writing hand tone 3","shortname":":writing_hand_tone3:","category":"people","emoji_order":"1233"},{"name":"writing hand tone 4","shortname":":writing_hand_tone4:","category":"people","emoji_order":"1234"},{"name":"writing hand tone 5","shortname":":writing_hand_tone5:","category":"people","emoji_order":"1235"},{"name":"open hands sign","shortname":":open_hands:","category":"people","emoji_order":"1236"},{"name":"open hands sign tone 1","shortname":":open_hands_tone1:","category":"people","emoji_order":"1237"},{"name":"open hands sign tone 2","shortname":":open_hands_tone2:","category":"people","emoji_order":"1238"},{"name":"open hands sign tone 3","shortname":":open_hands_tone3:","category":"people","emoji_order":"1239"},{"name":"open hands sign tone 4","shortname":":open_hands_tone4:","category":"people","emoji_order":"1240"},{"name":"open hands sign tone 5","shortname":":open_hands_tone5:","category":"people","emoji_order":"1241"},{"name":"person raising both hands in celebration","shortname":":raised_hands:","category":"people","emoji_order":"1242"},{"name":"person raising both hands in celebration tone 1","shortname":":raised_hands_tone1:","category":"people","emoji_order":"1243"},{"name":"person raising both hands in celebration tone 2","shortname":":raised_hands_tone2:","category":"people","emoji_order":"1244"},{"name":"person raising both hands in celebration tone 3","shortname":":raised_hands_tone3:","category":"people","emoji_order":"1245"},{"name":"person raising both hands in celebration tone 4","shortname":":raised_hands_tone4:","category":"people","emoji_order":"1246"},{"name":"person raising both hands in celebration tone 5","shortname":":raised_hands_tone5:","category":"people","emoji_order":"1247"},{"name":"person with folded hands","shortname":":pray:","category":"people","emoji_order":"1248"},{"name":"person with folded hands tone 1","shortname":":pray_tone1:","category":"people","emoji_order":"1249"},{"name":"person with folded hands tone 2","shortname":":pray_tone2:","category":"people","emoji_order":"1250"},{"name":"person with folded hands tone 3","shortname":":pray_tone3:","category":"people","emoji_order":"1251"},{"name":"person with folded hands tone 4","shortname":":pray_tone4:","category":"people","emoji_order":"1252"},{"name":"person with folded hands tone 5","shortname":":pray_tone5:","category":"people","emoji_order":"1253"},{"name":"handshake","shortname":":handshake:","category":"people","emoji_order":"1254","aliases":[":shaking_hands:"]},{"name":"handshake tone 1","shortname":":handshake_tone1:","category":"people","emoji_order":"1255","aliases":[":shaking_hands_tone1:"]},{"name":"handshake tone 2","shortname":":handshake_tone2:","category":"people","emoji_order":"1256","aliases":[":shaking_hands_tone2:"]},{"name":"handshake tone 3","shortname":":handshake_tone3:","category":"people","emoji_order":"1257","aliases":[":shaking_hands_tone3:"]},{"name":"handshake tone 4","shortname":":handshake_tone4:","category":"people","emoji_order":"1258","aliases":[":shaking_hands_tone4:"]},{"name":"handshake tone 5","shortname":":handshake_tone5:","category":"people","emoji_order":"1259","aliases":[":shaking_hands_tone5:"]},{"name":"nail polish","shortname":":nail_care:","category":"people","emoji_order":"1260"},{"name":"nail polish tone 1","shortname":":nail_care_tone1:","category":"people","emoji_order":"1261"},{"name":"nail polish tone 2","shortname":":nail_care_tone2:","category":"people","emoji_order":"1262"},{"name":"nail polish tone 3","shortname":":nail_care_tone3:","category":"people","emoji_order":"1263"},{"name":"nail polish tone 4","shortname":":nail_care_tone4:","category":"people","emoji_order":"1264"},{"name":"nail polish tone 5","shortname":":nail_care_tone5:","category":"people","emoji_order":"1265"},{"name":"ear","shortname":":ear:","category":"people","emoji_order":"1266"},{"name":"ear tone 1","shortname":":ear_tone1:","category":"people","emoji_order":"1267"},{"name":"ear tone 2","shortname":":ear_tone2:","category":"people","emoji_order":"1268"},{"name":"ear tone 3","shortname":":ear_tone3:","category":"people","emoji_order":"1269"},{"name":"ear tone 4","shortname":":ear_tone4:","category":"people","emoji_order":"1270"},{"name":"ear tone 5","shortname":":ear_tone5:","category":"people","emoji_order":"1271"},{"name":"nose","shortname":":nose:","category":"people","emoji_order":"1272"},{"name":"nose tone 1","shortname":":nose_tone1:","category":"people","emoji_order":"1273"},{"name":"nose tone 2","shortname":":nose_tone2:","category":"people","emoji_order":"1274"},{"name":"nose tone 3","shortname":":nose_tone3:","category":"people","emoji_order":"1275"},{"name":"nose tone 4","shortname":":nose_tone4:","category":"people","emoji_order":"1276"},{"name":"nose tone 5","shortname":":nose_tone5:","category":"people","emoji_order":"1277"},{"name":"footprints","shortname":":footprints:","category":"people","emoji_order":"1278"},{"name":"eyes","shortname":":eyes:","category":"people","emoji_order":"1279"},{"name":"eye","shortname":":eye:","category":"people","emoji_order":"1280"},{"name":"eye in speech bubble","shortname":":eye_in_speech_bubble:","category":"symbols","emoji_order":"1281"},{"name":"tongue","shortname":":tongue:","category":"people","emoji_order":"1282"},{"name":"mouth","shortname":":lips:","category":"people","emoji_order":"1283"},{"name":"kiss mark","shortname":":kiss:","category":"people","emoji_order":"1284"},{"name":"heart with arrow","shortname":":cupid:","category":"symbols","emoji_order":"1285"},{"name":"heavy black heart","shortname":":heart:","category":"symbols","emoji_order":"1286","aliases_ascii":["<3"]},{"name":"beating heart","shortname":":heartbeat:","category":"symbols","emoji_order":"1287"},{"name":"broken heart","shortname":":broken_heart:","category":"symbols","emoji_order":"1288","aliases_ascii":[""]},{"name":"smiling face with halo","shortname":":innocent:","category":0,"emoji_order":13,"aliases":[":halo:"],"aliases_ascii":["o:)"]},{"name":"smiling face with hearts","shortname":":love:","category":0,"emoji_order":14},{"name":"smiling face with heart-eyes","shortname":":lovestruck:","category":0,"emoji_order":15},{"name":"star-struck","shortname":":starstruck:","category":0,"emoji_order":16},{"name":"face blowing a kiss","shortname":":flirty:","category":0,"emoji_order":17,"aliases_ascii":[":x"]},{"name":"kissing face","shortname":":kiss:","category":0,"emoji_order":18},{"name":"smiling face","shortname":":relaxed:","category":0,"emoji_order":20},{"name":"kissing face with closed eyes","shortname":":loving_kiss:","category":0,"emoji_order":21,"aliases_ascii":[":*"]},{"name":"kissing face with smiling eyes","shortname":":happy_kiss:","category":0,"emoji_order":22},{"name":"face savoring food","shortname":":yum:","category":0,"emoji_order":23,"aliases":[":savour:"]},{"name":"face with tongue","shortname":":playful:","category":0,"emoji_order":24,"aliases":[":tongue_out:"],"aliases_ascii":[":p"]},{"name":"winking face with tongue","shortname":":mischievous:","category":0,"emoji_order":25,"aliases_ascii":[";p"]},{"name":"zany face","shortname":":crazy:","category":0,"emoji_order":26},{"name":"squinting face with tongue","shortname":":facetious:","category":0,"emoji_order":27,"aliases":[":lmao:"],"aliases_ascii":["xp"]},{"name":"money-mouth face","shortname":":pretentious:","category":0,"emoji_order":28,"aliases":[":money_mouth:"]},{"name":"hugging face","shortname":":hugging:","category":0,"emoji_order":29},{"name":"face with hand over mouth","shortname":":gasp:","category":0,"emoji_order":30},{"name":"shushing face","shortname":":shushing:","category":0,"emoji_order":31},{"name":"thinking face","shortname":":curious:","category":0,"emoji_order":32,"aliases":[":thinking:"],"aliases_ascii":[":l"]},{"name":"zipper-mouth face","shortname":":silenced:","category":0,"emoji_order":33,"aliases":[":zipper_mouth:"],"aliases_ascii":[":z"]},{"name":"face with raised eyebrow","shortname":":contempt:","category":0,"emoji_order":34},{"name":"neutral face","shortname":":indifferent:","category":0,"emoji_order":35,"aliases":[":neutral:"],"aliases_ascii":[":|"]},{"name":"expressionless face","shortname":":apathetic:","category":0,"emoji_order":36,"aliases":[":expressionless:"]},{"name":"face without mouth","shortname":":vacant:","category":0,"emoji_order":37,"aliases":[":no_mouth:"],"aliases_ascii":[":#"]},{"name":"smirking face","shortname":":cocky:","category":0,"emoji_order":38,"aliases":[":smirk:"],"aliases_ascii":[":j"]},{"name":"unamused face","shortname":":unamused:","category":0,"emoji_order":39,"aliases_ascii":[":?"]},{"name":"face with rolling eyes","shortname":":disbelief:","category":0,"emoji_order":40},{"name":"grimacing face","shortname":":grimaced:","category":0,"emoji_order":41,"aliases_ascii":["8D"]},{"name":"lying face","shortname":":lying:","category":0,"emoji_order":42},{"name":"relieved face","shortname":":relieved:","category":0,"emoji_order":43},{"name":"pensive face","shortname":":pensive:","category":0,"emoji_order":44},{"name":"sleepy face","shortname":":sleepy:","category":0,"emoji_order":45},{"name":"drooling face","shortname":":drooling:","category":0,"emoji_order":46},{"name":"sleeping face","shortname":":exhausted:","category":0,"emoji_order":47,"aliases":[":sleeping:"]},{"name":"face with medical mask","shortname":":ill:","category":0,"emoji_order":48,"aliases":[":mask:"]},{"name":"face with thermometer","shortname":":sick:","category":0,"emoji_order":49},{"name":"face with head-bandage","shortname":":injured:","category":0,"emoji_order":50},{"name":"nauseated face","shortname":":nauseated:","category":0,"emoji_order":51,"aliases_ascii":["%("]},{"name":"face vomiting","shortname":":vomiting:","category":0,"emoji_order":52},{"name":"sneezing face","shortname":":sneezing:","category":0,"emoji_order":53},{"name":"hot face","shortname":":overheating:","category":0,"emoji_order":54},{"name":"cold face","shortname":":freezing:","category":0,"emoji_order":55},{"name":"woozy face","shortname":":woozy:","category":0,"emoji_order":56,"aliases_ascii":[":&"]},{"name":"dizzy face","shortname":":dizzy:","category":0,"emoji_order":57,"aliases_ascii":["xo"]},{"name":"exploding head","shortname":":shocked:","category":0,"emoji_order":58,"aliases":[":exploding_head:"]},{"name":"cowboy hat face","shortname":":cowboy:","category":0,"emoji_order":59},{"name":"partying face","shortname":":partying:","category":0,"emoji_order":60,"aliases":[":celebrating:"]},{"name":"smiling face with sunglasses","shortname":":confident:","category":0,"emoji_order":61,"aliases_ascii":["8)"]},{"name":"nerd face","shortname":":nerd:","category":0,"emoji_order":62,"aliases_ascii":[":B"]},{"name":"face with monocle","shortname":":monocle:","category":0,"emoji_order":63},{"name":"confused face","shortname":":confused:","category":0,"emoji_order":64,"aliases_ascii":[":/"]},{"name":"worried face","shortname":":worried:","category":0,"emoji_order":65},{"name":"slightly frowning face","shortname":":cheerless:","category":0,"emoji_order":66},{"name":"frowning face","shortname":":sad:","category":0,"emoji_order":68,"aliases":[":frowning:"],"aliases_ascii":[":("]},{"name":"face with open mouth","shortname":":surprised:","category":0,"emoji_order":69},{"name":"hushed face","shortname":":hushed:","category":0,"emoji_order":70},{"name":"astonished face","shortname":":astonished:","category":0,"emoji_order":71,"aliases_ascii":[":o"]},{"name":"flushed face","shortname":":flushed:","category":0,"emoji_order":72,"aliases_ascii":[":$"]},{"name":"pleading face","shortname":":pleading:","category":0,"emoji_order":73},{"name":"frowning face with open mouth","shortname":":bored:","category":0,"emoji_order":74},{"name":"anguished face","shortname":":anguished:","category":0,"emoji_order":75,"aliases":[":wtf:"],"aliases_ascii":[":s"]},{"name":"fearful face","shortname":":fearful:","category":0,"emoji_order":76},{"name":"anxious face with sweat","shortname":":frustrated:","category":0,"emoji_order":77},{"name":"sad but relieved face","shortname":":hopeful:","category":0,"emoji_order":78},{"name":"crying face","shortname":":upset:","category":0,"emoji_order":79,"aliases":[":cry:"],"aliases_ascii":[":'("]},{"name":"loudly crying face","shortname":":distressed:","category":0,"emoji_order":80,"aliases":[":sob:"],"aliases_ascii":[":'o"]},{"name":"face screaming in fear","shortname":":frightened:","category":0,"emoji_order":81,"aliases":[":scream:"],"aliases_ascii":["Dx"]},{"name":"confounded face","shortname":":confounded:","category":0,"emoji_order":82,"aliases_ascii":["x("]},{"name":"persevering face","shortname":":persevered:","category":0,"emoji_order":83},{"name":"disappointed face","shortname":":disappointed:","category":0,"emoji_order":84},{"name":"downcast face with sweat","shortname":":shamed:","category":0,"emoji_order":85,"aliases_ascii":[":<"]},{"name":"weary face","shortname":":weary:","category":0,"emoji_order":86,"aliases_ascii":["D:"]},{"name":"tired face","shortname":":tired:","category":0,"emoji_order":87,"aliases_ascii":[":c"]},{"name":"yawning face","shortname":":yawn:","category":0,"emoji_order":88},{"name":"face with steam from nose","shortname":":annoyed:","category":0,"emoji_order":89,"aliases":[":hrmph:"]},{"name":"pouting face","shortname":":enraged:","category":0,"emoji_order":90,"aliases":[":pout:"],"aliases_ascii":[">:/"]},{"name":"angry face","shortname":":angry:","category":0,"emoji_order":91},{"name":"face with symbols on mouth","shortname":":censored:","category":0,"emoji_order":92,"aliases_ascii":[":@"]},{"name":"smiling face with horns","shortname":":imp:","category":0,"emoji_order":93,"aliases_ascii":[">:)"]},{"name":"angry face with horns","shortname":":angry_imp:","category":0,"emoji_order":94,"aliases_ascii":[">:("]},{"name":"skull","shortname":":skull:","category":0,"emoji_order":95},{"name":"skull and crossbones","shortname":":crossbones:","category":0,"emoji_order":97},{"name":"pile of poo","shortname":":poop:","category":0,"emoji_order":98},{"name":"clown face","shortname":":clown:","category":0,"emoji_order":99},{"name":"ogre","shortname":":ogre:","category":0,"emoji_order":100,"aliases_ascii":[">0)"]},{"name":"goblin","shortname":":goblin:","category":0,"emoji_order":101},{"name":"ghost","shortname":":ghost:","category":0,"emoji_order":102},{"name":"alien","shortname":":alien:","category":0,"emoji_order":103},{"name":"alien monster","shortname":":alien_monster:","category":0,"emoji_order":104,"aliases":[":space_invader:"]},{"name":"robot","shortname":":robot:","category":0,"emoji_order":105},{"name":"grinning cat","shortname":":smiling_cat:","category":0,"emoji_order":106},{"name":"grinning cat with smiling eyes","shortname":":grinning_cat:","category":0,"emoji_order":107},{"name":"cat with tears of joy","shortname":":joyful_cat:","category":0,"emoji_order":108},{"name":"smiling cat with heart-eyes","shortname":":lovestruck_cat:","category":0,"emoji_order":109},{"name":"cat with wry smile","shortname":":smirking_cat:","category":0,"emoji_order":110},{"name":"kissing cat","shortname":":kissing_cat:","category":0,"emoji_order":111,"aliases_ascii":[":3"]},{"name":"weary cat","shortname":":weary_cat:","category":0,"emoji_order":112},{"name":"crying cat","shortname":":crying_cat:","category":0,"emoji_order":113},{"name":"pouting cat","shortname":":pouting_cat:","category":0,"emoji_order":114},{"name":"see-no-evil monkey","shortname":":see_no_evil:","category":0,"emoji_order":115},{"name":"hear-no-evil monkey","shortname":":hear_no_evil:","category":0,"emoji_order":116},{"name":"speak-no-evil monkey","shortname":":speak_no_evil:","category":0,"emoji_order":117},{"name":"kiss mark","shortname":":kiss_lips:","category":0,"emoji_order":118},{"name":"love letter","shortname":":love_letter:","category":0,"emoji_order":119},{"name":"heart with arrow","shortname":":cupid:","category":0,"emoji_order":120},{"name":"heart with ribbon","shortname":":heart_ribbon:","category":0,"emoji_order":121},{"name":"sparkling heart","shortname":":sparkling_heart:","category":0,"emoji_order":122},{"name":"growing heart","shortname":":heartpulse:","category":0,"emoji_order":123},{"name":"beating heart","shortname":":heartbeat:","category":0,"emoji_order":124},{"name":"revolving hearts","shortname":":revolving_hearts:","category":0,"emoji_order":125},{"name":"two hearts","shortname":":two_hearts:","category":0,"emoji_order":126},{"name":"heart decoration","shortname":":heart_decoration:","category":0,"emoji_order":127},{"name":"heart exclamation","shortname":":heart_exclamation:","category":0,"emoji_order":129},{"name":"broken heart","shortname":":broken_heart:","category":0,"emoji_order":130,"aliases_ascii":[""]},{"name":"woman mage","shortname":":woman_mage:","category":1,"emoji_order":1375},{"name":"fairy","shortname":":fairy:","category":1,"emoji_order":1387},{"name":"man fairy","shortname":":man_fairy:","category":1,"emoji_order":1393},{"name":"woman fairy","shortname":":woman_fairy:","category":1,"emoji_order":1405},{"name":"vampire","shortname":":vampire:","category":1,"emoji_order":1417,"aliases_ascii":[":E"]},{"name":"man vampire","shortname":":man_vampire:","category":1,"emoji_order":1423},{"name":"woman vampire","shortname":":woman_vampire:","category":1,"emoji_order":1435},{"name":"merperson","shortname":":merperson:","category":1,"emoji_order":1447},{"name":"merman","shortname":":merman:","category":1,"emoji_order":1453},{"name":"mermaid","shortname":":mermaid:","category":1,"emoji_order":1465},{"name":"elf","shortname":":elf:","category":1,"emoji_order":1477},{"name":"man elf","shortname":":man_elf:","category":1,"emoji_order":1483},{"name":"woman elf","shortname":":woman_elf:","category":1,"emoji_order":1495},{"name":"genie","shortname":":genie:","category":1,"emoji_order":1507},{"name":"man genie","shortname":":man_genie:","category":1,"emoji_order":1508},{"name":"woman genie","shortname":":woman_genie:","category":1,"emoji_order":1510},{"name":"zombie","shortname":":zombie:","category":1,"emoji_order":1512,"aliases_ascii":["8#"]},{"name":"man zombie","shortname":":man_zombie:","category":1,"emoji_order":1513},{"name":"woman zombie","shortname":":woman_zombie:","category":1,"emoji_order":1515},{"name":"person getting massage","shortname":":person_getting_massage:","category":1,"emoji_order":1517},{"name":"man getting massage","shortname":":man_getting_face_massage:","category":1,"emoji_order":1523},{"name":"woman getting massage","shortname":":woman_getting_face_massage:","category":1,"emoji_order":1535},{"name":"person getting haircut","shortname":":person_getting_haircut:","category":1,"emoji_order":1547},{"name":"man getting haircut","shortname":":man_getting_haircut:","category":1,"emoji_order":1553},{"name":"woman getting haircut","shortname":":woman_getting_haircut:","category":1,"emoji_order":1565},{"name":"person walking","shortname":":person_walking:","category":1,"emoji_order":1577},{"name":"man walking","shortname":":man_walking:","category":1,"emoji_order":1583},{"name":"woman walking","shortname":":woman_walking:","category":1,"emoji_order":1595},{"name":"person standing","shortname":":person_standing:","category":1,"emoji_order":1607},{"name":"man standing","shortname":":man_standing:","category":1,"emoji_order":1613},{"name":"woman standing","shortname":":woman_standing:","category":1,"emoji_order":1625},{"name":"person kneeling","shortname":":person_kneeling:","category":1,"emoji_order":1637},{"name":"man kneeling","shortname":":man_kneeling:","category":1,"emoji_order":1643},{"name":"woman kneeling","shortname":":woman_kneeling:","category":1,"emoji_order":1655},{"name":"man with probing cane","shortname":":man_probing_cane:","category":1,"emoji_order":1667},{"name":"woman with probing cane","shortname":":woman_probing_cane:","category":1,"emoji_order":1673},{"name":"man in motorized wheelchair","shortname":":man_motor_wheelchair:","category":1,"emoji_order":1679},{"name":"woman in motorized wheelchair","shortname":":woman_motor_wheelchair:","category":1,"emoji_order":1685},{"name":"man in manual wheelchair","shortname":":man_wheelchair:","category":1,"emoji_order":1691},{"name":"woman in manual wheelchair","shortname":":woman_wheelchair:","category":1,"emoji_order":1697},{"name":"person running","shortname":":person_running:","category":1,"emoji_order":1703},{"name":"man running","shortname":":man_running:","category":1,"emoji_order":1709},{"name":"woman running","shortname":":woman_running:","category":1,"emoji_order":1721},{"name":"woman dancing","shortname":":dancer:","category":1,"emoji_order":1733,"aliases":[":woman_dancing:"]},{"name":"man dancing","shortname":":man_dancing:","category":1,"emoji_order":1739},{"name":"man in suit levitating","shortname":":levitate:","category":1,"emoji_order":1746},{"name":"people with bunny ears","shortname":":people_bunny_ears_partying:","category":1,"emoji_order":1752},{"name":"men with bunny ears","shortname":":men_bunny_ears_partying:","category":1,"emoji_order":1753},{"name":"women with bunny ears","shortname":":women_bunny_ears_partying:","category":1,"emoji_order":1755},{"name":"person in steamy room","shortname":":person_steamy_room:","category":1,"emoji_order":1757},{"name":"man in steamy room","shortname":":man_steamy_room:","category":1,"emoji_order":1763},{"name":"woman in steamy room","shortname":":woman_steamy_room:","category":1,"emoji_order":1775},{"name":"person climbing","shortname":":person_climbing:","category":1,"emoji_order":1787},{"name":"man climbing","shortname":":man_climbing:","category":1,"emoji_order":1793},{"name":"woman climbing","shortname":":woman_climbing:","category":1,"emoji_order":1805},{"name":"person fencing","shortname":":person_fencing:","category":1,"emoji_order":1817},{"name":"horse racing","shortname":":horse_racing:","category":1,"emoji_order":1818},{"name":"skier","shortname":":skier:","category":1,"emoji_order":1825},{"name":"snowboarder","shortname":":snowboarder:","category":1,"emoji_order":1826},{"name":"person golfing","shortname":":person_golfing:","category":1,"emoji_order":1833},{"name":"man golfing","shortname":":man_golfing:","category":1,"emoji_order":1839},{"name":"woman golfing","shortname":":woman_golfing:","category":1,"emoji_order":1853},{"name":"person surfing","shortname":":person_surfing:","category":1,"emoji_order":1867},{"name":"man surfing","shortname":":man_surfing:","category":1,"emoji_order":1873},{"name":"woman surfing","shortname":":woman_surfing:","category":1,"emoji_order":1885},{"name":"person rowing boat","shortname":":person_rowing_boat:","category":1,"emoji_order":1897},{"name":"man rowing boat","shortname":":man_rowing_boat:","category":1,"emoji_order":1903},{"name":"woman rowing boat","shortname":":woman_rowing_boat:","category":1,"emoji_order":1915},{"name":"person swimming","shortname":":person_swimming:","category":1,"emoji_order":1927},{"name":"man swimming","shortname":":man_swimming:","category":1,"emoji_order":1933},{"name":"woman swimming","shortname":":woman_swimming:","category":1,"emoji_order":1945},{"name":"person bouncing ball","shortname":":person_bouncing_ball:","category":1,"emoji_order":1958},{"name":"man bouncing ball","shortname":":man_bouncing_ball:","category":1,"emoji_order":1964},{"name":"woman bouncing ball","shortname":":woman_bouncing_ball:","category":1,"emoji_order":1978},{"name":"person lifting weights","shortname":":person_lifting_weights:","category":1,"emoji_order":1993},{"name":"man lifting weights","shortname":":man_lifting_weights:","category":1,"emoji_order":1999},{"name":"woman lifting weights","shortname":":woman_lifting_weights:","category":1,"emoji_order":2013},{"name":"person biking","shortname":":person_biking:","category":1,"emoji_order":2027},{"name":"man biking","shortname":":man_biking:","category":1,"emoji_order":2033},{"name":"woman biking","shortname":":woman_biking:","category":1,"emoji_order":2045},{"name":"person mountain biking","shortname":":person_mountain_biking:","category":1,"emoji_order":2057},{"name":"man mountain biking","shortname":":man_mountain_biking:","category":1,"emoji_order":2063},{"name":"woman mountain biking","shortname":":woman_mountain_biking:","category":1,"emoji_order":2075},{"name":"person cartwheeling","shortname":":person_cartwheel:","category":1,"emoji_order":2087},{"name":"man cartwheeling","shortname":":man_cartwheeling:","category":1,"emoji_order":2093},{"name":"woman cartwheeling","shortname":":woman_cartwheeling:","category":1,"emoji_order":2105},{"name":"people wrestling","shortname":":people_wrestling:","category":1,"emoji_order":2117},{"name":"men wrestling","shortname":":men_wrestling:","category":1,"emoji_order":2118},{"name":"women wrestling","shortname":":women_wrestling:","category":1,"emoji_order":2120},{"name":"person playing water polo","shortname":":person_water_polo:","category":1,"emoji_order":2122},{"name":"man playing water polo","shortname":":man_water_polo:","category":1,"emoji_order":2128},{"name":"woman playing water polo","shortname":":woman_water_polo:","category":1,"emoji_order":2140},{"name":"person playing handball","shortname":":person_handball:","category":1,"emoji_order":2152},{"name":"man playing handball","shortname":":man_handball:","category":1,"emoji_order":2158},{"name":"woman playing handball","shortname":":woman_handball:","category":1,"emoji_order":2170},{"name":"person juggling","shortname":":person_juggling:","category":1,"emoji_order":2182},{"name":"man juggling","shortname":":man_juggling:","category":1,"emoji_order":2188},{"name":"woman juggling","shortname":":woman_juggling:","category":1,"emoji_order":2200},{"name":"person in lotus position","shortname":":person_lotus_position:","category":1,"emoji_order":2212},{"name":"man in lotus position","shortname":":man_lotus_position:","category":1,"emoji_order":2218},{"name":"woman in lotus position","shortname":":woman_lotus_position:","category":1,"emoji_order":2230},{"name":"person taking bath","shortname":":bath:","category":1,"emoji_order":2242},{"name":"person in bed","shortname":":in_bed:","category":1,"emoji_order":2248},{"name":"people holding hands","shortname":":holding_hands_people:","category":1,"emoji_order":2254},{"name":"women holding hands","shortname":":holding_hands_ww:","category":1,"emoji_order":2270},{"name":"woman and man holding hands","shortname":":holding_hands_mw:","category":1,"emoji_order":2286,"aliases":[":holding_hands_wm:"]},{"name":"men holding hands","shortname":":holding_hands_mm:","category":1,"emoji_order":2312},{"name":"kiss","shortname":":couple:","category":1,"emoji_order":2328},{"name":"kiss: woman, man","shortname":":kiss_mw:","category":1,"emoji_order":2329,"aliases":[":kiss_wm:"]},{"name":"kiss: man, man","shortname":":kiss_mm:","category":1,"emoji_order":2331},{"name":"kiss: woman, woman","shortname":":kiss_ww:","category":1,"emoji_order":2333},{"name":"couple with heart","shortname":":couple_heart:","category":1,"emoji_order":2335},{"name":"couple with heart: woman, man","shortname":":couple_mw:","category":1,"emoji_order":2336,"aliases":[":couple_wm:"]},{"name":"couple with heart: man, man","shortname":":couple_mm:","category":1,"emoji_order":2338},{"name":"couple with heart: woman, woman","shortname":":couple_ww:","category":1,"emoji_order":2340},{"name":"family","shortname":":family:","category":1,"emoji_order":2342},{"name":"family: man, woman, boy","shortname":":family_mwb:","category":1,"emoji_order":2343},{"name":"family: man, woman, girl","shortname":":family_mwg:","category":1,"emoji_order":2344},{"name":"family: man, woman, girl, boy","shortname":":family_mwgb:","category":1,"emoji_order":2345},{"name":"family: man, woman, boy, boy","shortname":":family_mwbb:","category":1,"emoji_order":2346},{"name":"family: man, woman, girl, girl","shortname":":family_mwgg:","category":1,"emoji_order":2347},{"name":"family: man, man, boy","shortname":":family_mmb:","category":1,"emoji_order":2348},{"name":"family: man, man, girl","shortname":":family_mmg:","category":1,"emoji_order":2349},{"name":"family: man, man, girl, boy","shortname":":family_mmgb:","category":1,"emoji_order":2350},{"name":"family: man, man, boy, boy","shortname":":family_mmbb:","category":1,"emoji_order":2351},{"name":"family: man, man, girl, girl","shortname":":family_mmgg:","category":1,"emoji_order":2352},{"name":"family: woman, woman, boy","shortname":":family_wwb:","category":1,"emoji_order":2353},{"name":"family: woman, woman, girl","shortname":":family_wwg:","category":1,"emoji_order":2354},{"name":"family: woman, woman, girl, boy","shortname":":family_wwgb:","category":1,"emoji_order":2355},{"name":"family: woman, woman, boy, boy","shortname":":family_wwbb:","category":1,"emoji_order":2356},{"name":"family: woman, woman, girl, girl","shortname":":family_wwgg:","category":1,"emoji_order":2357},{"name":"family: man, boy","shortname":":family_mb:","category":1,"emoji_order":2358},{"name":"family: man, boy, boy","shortname":":family_mbb:","category":1,"emoji_order":2359},{"name":"family: man, girl","shortname":":family_mg:","category":1,"emoji_order":2360},{"name":"family: man, girl, boy","shortname":":family_mgb:","category":1,"emoji_order":2361},{"name":"family: man, girl, girl","shortname":":family_mgg:","category":1,"emoji_order":2362},{"name":"family: woman, boy","shortname":":family_wb:","category":1,"emoji_order":2363},{"name":"family: woman, boy, boy","shortname":":family_wbb:","category":1,"emoji_order":2364},{"name":"family: woman, girl","shortname":":family_wg:","category":1,"emoji_order":2365},{"name":"family: woman, girl, boy","shortname":":family_wgb:","category":1,"emoji_order":2366},{"name":"family: woman, girl, girl","shortname":":family_wgg:","category":1,"emoji_order":2367},{"name":"speaking head","shortname":":speaking_head:","category":1,"emoji_order":2369},{"name":"bust in silhouette","shortname":":bust_silhouette:","category":1,"emoji_order":2370},{"name":"busts in silhouette","shortname":":busts_silhouette:","category":1,"emoji_order":2371},{"name":"footprints","shortname":":footprints:","category":1,"emoji_order":2372},{"name":"light skin tone","shortname":":tone_light:","category":2,"emoji_order":2373,"aliases":[":tone1:"]},{"name":"medium-light skin tone","shortname":":tone_medium_light:","category":2,"emoji_order":2374,"aliases":[":tone2:"]},{"name":"medium skin tone","shortname":":tone_medium:","category":2,"emoji_order":2375,"aliases":[":tone3:"]},{"name":"medium-dark skin tone","shortname":":tone_medium_dark:","category":2,"emoji_order":2376,"aliases":[":tone4:"]},{"name":"dark skin tone","shortname":":tone_dark:","category":2,"emoji_order":2377,"aliases":[":tone5:"]},{"name":"red hair","shortname":":red_hair:","category":2,"emoji_order":2378},{"name":"curly hair","shortname":":curly_hair:","category":2,"emoji_order":2379},{"name":"white hair","shortname":":white_hair:","category":2,"emoji_order":2380},{"name":"bald","shortname":":bald:","category":2,"emoji_order":2381},{"name":"monkey face","shortname":":monkey_face:","category":3,"emoji_order":2382},{"name":"monkey","shortname":":monkey:","category":3,"emoji_order":2383},{"name":"gorilla","shortname":":gorilla:","category":3,"emoji_order":2384},{"name":"orangutan","shortname":":orangutan:","category":3,"emoji_order":2385},{"name":"dog face","shortname":":dog_face:","category":3,"emoji_order":2386},{"name":"dog","shortname":":dog:","category":3,"emoji_order":2387},{"name":"guide dog","shortname":":guide_dog:","category":3,"emoji_order":2388},{"name":"service dog","shortname":":service_dog:","category":3,"emoji_order":2389},{"name":"poodle","shortname":":poodle:","category":3,"emoji_order":2390},{"name":"wolf","shortname":":wolf_face:","category":3,"emoji_order":2391},{"name":"fox","shortname":":fox_face:","category":3,"emoji_order":2392},{"name":"raccoon","shortname":":raccoon:","category":3,"emoji_order":2393},{"name":"cat face","shortname":":cat_face:","category":3,"emoji_order":2394},{"name":"cat","shortname":":cat:","category":3,"emoji_order":2395},{"name":"lion","shortname":":lion_face:","category":3,"emoji_order":2396},{"name":"tiger face","shortname":":tiger_face:","category":3,"emoji_order":2397},{"name":"tiger","shortname":":tiger:","category":3,"emoji_order":2398},{"name":"leopard","shortname":":leopard:","category":3,"emoji_order":2399},{"name":"horse face","shortname":":horse_face:","category":3,"emoji_order":2400},{"name":"horse","shortname":":horse:","category":3,"emoji_order":2401},{"name":"unicorn","shortname":":unicorn_face:","category":3,"emoji_order":2402},{"name":"zebra","shortname":":zebra:","category":3,"emoji_order":2403},{"name":"deer","shortname":":deer:","category":3,"emoji_order":2404},{"name":"cow face","shortname":":cow_face:","category":3,"emoji_order":2405},{"name":"ox","shortname":":ox:","category":3,"emoji_order":2406},{"name":"water buffalo","shortname":":water_buffalo:","category":3,"emoji_order":2407},{"name":"cow","shortname":":cow:","category":3,"emoji_order":2408},{"name":"pig face","shortname":":pig_face:","category":3,"emoji_order":2409},{"name":"pig","shortname":":pig:","category":3,"emoji_order":2410},{"name":"boar","shortname":":boar:","category":3,"emoji_order":2411},{"name":"pig nose","shortname":":pig_nose:","category":3,"emoji_order":2412},{"name":"ram","shortname":":ram:","category":3,"emoji_order":2413},{"name":"ewe","shortname":":sheep:","category":3,"emoji_order":2414},{"name":"goat","shortname":":goat:","category":3,"emoji_order":2415},{"name":"camel","shortname":":camel:","category":3,"emoji_order":2416},{"name":"two-hump camel","shortname":":two_hump_camel:","category":3,"emoji_order":2417},{"name":"llama","shortname":":llama:","category":3,"emoji_order":2418},{"name":"giraffe","shortname":":giraffe:","category":3,"emoji_order":2419},{"name":"elephant","shortname":":elephant:","category":3,"emoji_order":2420},{"name":"rhinoceros","shortname":":rhino:","category":3,"emoji_order":2421},{"name":"hippopotamus","shortname":":hippo:","category":3,"emoji_order":2422},{"name":"mouse face","shortname":":mouse_face:","category":3,"emoji_order":2423},{"name":"mouse","shortname":":mouse:","category":3,"emoji_order":2424},{"name":"rat","shortname":":rat:","category":3,"emoji_order":2425},{"name":"hamster","shortname":":hamster_face:","category":3,"emoji_order":2426},{"name":"rabbit face","shortname":":rabbit_face:","category":3,"emoji_order":2427},{"name":"rabbit","shortname":":rabbit:","category":3,"emoji_order":2428},{"name":"chipmunk","shortname":":chipmunk:","category":3,"emoji_order":2430},{"name":"hedgehog","shortname":":hedgehog:","category":3,"emoji_order":2431},{"name":"bat","shortname":":bat:","category":3,"emoji_order":2432},{"name":"bear","shortname":":bear_face:","category":3,"emoji_order":2433},{"name":"koala","shortname":":koala_face:","category":3,"emoji_order":2434},{"name":"panda","shortname":":panda_face:","category":3,"emoji_order":2435},{"name":"sloth","shortname":":sloth:","category":3,"emoji_order":2436},{"name":"otter","shortname":":otter:","category":3,"emoji_order":2437},{"name":"skunk","shortname":":skunk:","category":3,"emoji_order":2438},{"name":"kangaroo","shortname":":kangaroo:","category":3,"emoji_order":2439},{"name":"badger","shortname":":badger:","category":3,"emoji_order":2440},{"name":"paw prints","shortname":":feet:","category":3,"emoji_order":2441},{"name":"turkey","shortname":":turkey:","category":3,"emoji_order":2442},{"name":"chicken","shortname":":chicken:","category":3,"emoji_order":2443},{"name":"rooster","shortname":":rooster:","category":3,"emoji_order":2444},{"name":"hatching chick","shortname":":hatching_chick:","category":3,"emoji_order":2445},{"name":"baby chick","shortname":":baby_chick:","category":3,"emoji_order":2446},{"name":"front-facing baby chick","shortname":":hatched_chick:","category":3,"emoji_order":2447},{"name":"bird","shortname":":bird:","category":3,"emoji_order":2448},{"name":"penguin","shortname":":penguin:","category":3,"emoji_order":2449},{"name":"dove","shortname":":dove:","category":3,"emoji_order":2451},{"name":"eagle","shortname":":eagle:","category":3,"emoji_order":2452},{"name":"duck","shortname":":duck:","category":3,"emoji_order":2453},{"name":"swan","shortname":":swan:","category":3,"emoji_order":2454},{"name":"owl","shortname":":owl:","category":3,"emoji_order":2455},{"name":"flamingo","shortname":":flamingo:","category":3,"emoji_order":2456},{"name":"peacock","shortname":":peacock:","category":3,"emoji_order":2457},{"name":"parrot","shortname":":parrot:","category":3,"emoji_order":2458},{"name":"frog","shortname":":frog_face:","category":3,"emoji_order":2459},{"name":"crocodile","shortname":":crocodile:","category":3,"emoji_order":2460},{"name":"turtle","shortname":":turtle:","category":3,"emoji_order":2461},{"name":"lizard","shortname":":lizard:","category":3,"emoji_order":2462},{"name":"snake","shortname":":snake:","category":3,"emoji_order":2463},{"name":"dragon face","shortname":":dragon_face:","category":3,"emoji_order":2464},{"name":"dragon","shortname":":dragon:","category":3,"emoji_order":2465},{"name":"sauropod","shortname":":sauropod:","category":3,"emoji_order":2466},{"name":"T-Rex","shortname":":trex:","category":3,"emoji_order":2467},{"name":"spouting whale","shortname":":spouting_whale:","category":3,"emoji_order":2468},{"name":"whale","shortname":":whale:","category":3,"emoji_order":2469},{"name":"dolphin","shortname":":dolphin:","category":3,"emoji_order":2470},{"name":"fish","shortname":":fish:","category":3,"emoji_order":2471},{"name":"tropical fish","shortname":":tropical_fish:","category":3,"emoji_order":2472},{"name":"blowfish","shortname":":blowfish:","category":3,"emoji_order":2473},{"name":"shark","shortname":":shark:","category":3,"emoji_order":2474},{"name":"octopus","shortname":":octopus:","category":3,"emoji_order":2475},{"name":"spiral shell","shortname":":shell:","category":3,"emoji_order":2476},{"name":"snail","shortname":":snail:","category":3,"emoji_order":2477},{"name":"butterfly","shortname":":butterfly:","category":3,"emoji_order":2478},{"name":"bug","shortname":":bug:","category":3,"emoji_order":2479},{"name":"ant","shortname":":ant:","category":3,"emoji_order":2480},{"name":"honeybee","shortname":":bee:","category":3,"emoji_order":2481},{"name":"lady beetle","shortname":":beetle:","category":3,"emoji_order":2482},{"name":"cricket","shortname":":cricket:","category":3,"emoji_order":2483},{"name":"spider","shortname":":spider:","category":3,"emoji_order":2485},{"name":"spider web","shortname":":spider_web:","category":3,"emoji_order":2487},{"name":"scorpion","shortname":":scorpion:","category":3,"emoji_order":2488},{"name":"mosquito","shortname":":mosquito:","category":3,"emoji_order":2489},{"name":"microbe","shortname":":microbe:","category":3,"emoji_order":2490,"aliases":[":germ:"]},{"name":"bouquet","shortname":":bouquet:","category":3,"emoji_order":2491},{"name":"cherry blossom","shortname":":cherry_blossom:","category":3,"emoji_order":2492},{"name":"white flower","shortname":":white_flower:","category":3,"emoji_order":2493},{"name":"rosette","shortname":":rosette:","category":3,"emoji_order":2495},{"name":"rose","shortname":":rose:","category":3,"emoji_order":2496},{"name":"wilted flower","shortname":":wilted_rose:","category":3,"emoji_order":2497},{"name":"hibiscus","shortname":":hibiscus:","category":3,"emoji_order":2498},{"name":"sunflower","shortname":":sunflower:","category":3,"emoji_order":2499},{"name":"blossom","shortname":":blossom:","category":3,"emoji_order":2500},{"name":"tulip","shortname":":tulip:","category":3,"emoji_order":2501},{"name":"seedling","shortname":":seedling:","category":3,"emoji_order":2502},{"name":"evergreen tree","shortname":":evergreen_tree:","category":3,"emoji_order":2503},{"name":"deciduous tree","shortname":":deciduous_tree:","category":3,"emoji_order":2504},{"name":"palm tree","shortname":":palm_tree:","category":3,"emoji_order":2505},{"name":"cactus","shortname":":cactus:","category":3,"emoji_order":2506},{"name":"sheaf of rice","shortname":":ear_of_rice:","category":3,"emoji_order":2507},{"name":"herb","shortname":":herb:","category":3,"emoji_order":2508},{"name":"shamrock","shortname":":shamrock:","category":3,"emoji_order":2510},{"name":"four leaf clover","shortname":":four_leaf_clover:","category":3,"emoji_order":2511},{"name":"maple leaf","shortname":":maple_leaf:","category":3,"emoji_order":2512},{"name":"fallen leaf","shortname":":fallen_leaf:","category":3,"emoji_order":2513},{"name":"leaf fluttering in wind","shortname":":leaves:","category":3,"emoji_order":2514},{"name":"grapes","shortname":":grapes:","category":4,"emoji_order":2515},{"name":"melon","shortname":":melon:","category":4,"emoji_order":2516},{"name":"watermelon","shortname":":watermelon:","category":4,"emoji_order":2517},{"name":"tangerine","shortname":":tangerine:","category":4,"emoji_order":2518},{"name":"lemon","shortname":":lemon:","category":4,"emoji_order":2519},{"name":"banana","shortname":":banana:","category":4,"emoji_order":2520},{"name":"pineapple","shortname":":pineapple:","category":4,"emoji_order":2521},{"name":"mango","shortname":":mango:","category":4,"emoji_order":2522},{"name":"red apple","shortname":":apple:","category":4,"emoji_order":2523},{"name":"green apple","shortname":":green_apple:","category":4,"emoji_order":2524},{"name":"pear","shortname":":pear:","category":4,"emoji_order":2525},{"name":"peach","shortname":":peach:","category":4,"emoji_order":2526},{"name":"cherries","shortname":":cherries:","category":4,"emoji_order":2527},{"name":"strawberry","shortname":":strawberry:","category":4,"emoji_order":2528},{"name":"kiwi fruit","shortname":":kiwi:","category":4,"emoji_order":2529},{"name":"tomato","shortname":":tomato:","category":4,"emoji_order":2530},{"name":"coconut","shortname":":coconut:","category":4,"emoji_order":2531},{"name":"avocado","shortname":":avocado:","category":4,"emoji_order":2532},{"name":"eggplant","shortname":":eggplant:","category":4,"emoji_order":2533},{"name":"potato","shortname":":potato:","category":4,"emoji_order":2534},{"name":"carrot","shortname":":carrot:","category":4,"emoji_order":2535},{"name":"ear of corn","shortname":":corn:","category":4,"emoji_order":2536},{"name":"hot pepper","shortname":":hot_pepper:","category":4,"emoji_order":2538},{"name":"cucumber","shortname":":cucumber:","category":4,"emoji_order":2539},{"name":"leafy green","shortname":":leafy_green:","category":4,"emoji_order":2540},{"name":"broccoli","shortname":":broccoli:","category":4,"emoji_order":2541},{"name":"garlic","shortname":":garlic:","category":4,"emoji_order":2542},{"name":"onion","shortname":":onion:","category":4,"emoji_order":2543},{"name":"mushroom","shortname":":mushroom:","category":4,"emoji_order":2544},{"name":"peanuts","shortname":":peanuts:","category":4,"emoji_order":2545},{"name":"chestnut","shortname":":chestnut:","category":4,"emoji_order":2546},{"name":"bread","shortname":":bread:","category":4,"emoji_order":2547},{"name":"croissant","shortname":":croissant:","category":4,"emoji_order":2548},{"name":"baguette bread","shortname":":french_bread:","category":4,"emoji_order":2549},{"name":"pretzel","shortname":":pretzel:","category":4,"emoji_order":2550},{"name":"bagel","shortname":":bagel:","category":4,"emoji_order":2551},{"name":"pancakes","shortname":":pancakes:","category":4,"emoji_order":2552},{"name":"waffle","shortname":":waffle:","category":4,"emoji_order":2553},{"name":"cheese wedge","shortname":":cheese:","category":4,"emoji_order":2554},{"name":"meat on bone","shortname":":meat_on_bone:","category":4,"emoji_order":2555},{"name":"poultry leg","shortname":":poultry_leg:","category":4,"emoji_order":2556},{"name":"cut of meat","shortname":":cut_of_meat:","category":4,"emoji_order":2557},{"name":"bacon","shortname":":bacon:","category":4,"emoji_order":2558},{"name":"hamburger","shortname":":hamburger:","category":4,"emoji_order":2559},{"name":"french fries","shortname":":fries:","category":4,"emoji_order":2560},{"name":"pizza","shortname":":pizza:","category":4,"emoji_order":2561},{"name":"hot dog","shortname":":hotdog:","category":4,"emoji_order":2562},{"name":"sandwich","shortname":":sandwich:","category":4,"emoji_order":2563},{"name":"taco","shortname":":taco:","category":4,"emoji_order":2564},{"name":"burrito","shortname":":burrito:","category":4,"emoji_order":2565},{"name":"stuffed flatbread","shortname":":stuffed_flatbread:","category":4,"emoji_order":2566},{"name":"falafel","shortname":":falafel:","category":4,"emoji_order":2567},{"name":"egg","shortname":":egg:","category":4,"emoji_order":2568},{"name":"cooking","shortname":":cooking:","category":4,"emoji_order":2569},{"name":"shallow pan of food","shortname":":shallow_pan_of_food:","category":4,"emoji_order":2570},{"name":"pot of food","shortname":":stew:","category":4,"emoji_order":2571},{"name":"bowl with spoon","shortname":":bowl_spoon:","category":4,"emoji_order":2572},{"name":"green salad","shortname":":salad:","category":4,"emoji_order":2573},{"name":"popcorn","shortname":":popcorn:","category":4,"emoji_order":2574},{"name":"butter","shortname":":butter:","category":4,"emoji_order":2575},{"name":"salt","shortname":":salt:","category":4,"emoji_order":2576},{"name":"canned food","shortname":":canned_food:","category":4,"emoji_order":2577},{"name":"bento box","shortname":":bento:","category":4,"emoji_order":2578},{"name":"rice cracker","shortname":":rice_cracker:","category":4,"emoji_order":2579},{"name":"rice ball","shortname":":rice_ball:","category":4,"emoji_order":2580},{"name":"cooked rice","shortname":":rice:","category":4,"emoji_order":2581},{"name":"curry rice","shortname":":curry:","category":4,"emoji_order":2582},{"name":"steaming bowl","shortname":":ramen:","category":4,"emoji_order":2583},{"name":"spaghetti","shortname":":spaghetti:","category":4,"emoji_order":2584},{"name":"roasted sweet potato","shortname":":sweet_potato:","category":4,"emoji_order":2585},{"name":"oden","shortname":":oden:","category":4,"emoji_order":2586},{"name":"sushi","shortname":":sushi:","category":4,"emoji_order":2587},{"name":"fried shrimp","shortname":":fried_shrimp:","category":4,"emoji_order":2588},{"name":"fish cake with swirl","shortname":":fish_cake:","category":4,"emoji_order":2589},{"name":"moon cake","shortname":":moon_cake:","category":4,"emoji_order":2590},{"name":"dango","shortname":":dango:","category":4,"emoji_order":2591},{"name":"dumpling","shortname":":dumpling:","category":4,"emoji_order":2592},{"name":"fortune cookie","shortname":":fortune_cookie:","category":4,"emoji_order":2593},{"name":"takeout box","shortname":":takeout_box:","category":4,"emoji_order":2594},{"name":"crab","shortname":":crab:","category":4,"emoji_order":2595},{"name":"lobster","shortname":":lobster:","category":4,"emoji_order":2596},{"name":"shrimp","shortname":":shrimp:","category":4,"emoji_order":2597},{"name":"squid","shortname":":squid:","category":4,"emoji_order":2598},{"name":"oyster","shortname":":oyster:","category":4,"emoji_order":2599},{"name":"soft ice cream","shortname":":icecream:","category":4,"emoji_order":2600},{"name":"shaved ice","shortname":":shaved_ice:","category":4,"emoji_order":2601},{"name":"ice cream","shortname":":ice_cream:","category":4,"emoji_order":2602},{"name":"doughnut","shortname":":doughnut:","category":4,"emoji_order":2603},{"name":"cookie","shortname":":cookie:","category":4,"emoji_order":2604},{"name":"birthday cake","shortname":":birthday:","category":4,"emoji_order":2605},{"name":"shortcake","shortname":":cake:","category":4,"emoji_order":2606},{"name":"cupcake","shortname":":cupcake:","category":4,"emoji_order":2607},{"name":"pie","shortname":":pie:","category":4,"emoji_order":2608},{"name":"chocolate bar","shortname":":chocolate_bar:","category":4,"emoji_order":2609},{"name":"candy","shortname":":candy:","category":4,"emoji_order":2610},{"name":"lollipop","shortname":":lollipop:","category":4,"emoji_order":2611},{"name":"custard","shortname":":custard:","category":4,"emoji_order":2612},{"name":"honey pot","shortname":":honey_pot:","category":4,"emoji_order":2613},{"name":"baby bottle","shortname":":baby_bottle:","category":4,"emoji_order":2614},{"name":"glass of milk","shortname":":milk:","category":4,"emoji_order":2615},{"name":"hot beverage","shortname":":coffee:","category":4,"emoji_order":2616},{"name":"teacup without handle","shortname":":tea:","category":4,"emoji_order":2617},{"name":"sake","shortname":":sake:","category":4,"emoji_order":2618},{"name":"bottle with popping cork","shortname":":champagne:","category":4,"emoji_order":2619},{"name":"wine glass","shortname":":wine_glass:","category":4,"emoji_order":2620},{"name":"cocktail glass","shortname":":cocktail:","category":4,"emoji_order":2621},{"name":"tropical drink","shortname":":tropical_drink:","category":4,"emoji_order":2622},{"name":"beer mug","shortname":":beer:","category":4,"emoji_order":2623},{"name":"clinking beer mugs","shortname":":beers:","category":4,"emoji_order":2624},{"name":"clinking glasses","shortname":":champagne_glass:","category":4,"emoji_order":2625},{"name":"tumbler glass","shortname":":tumbler_glass:","category":4,"emoji_order":2626},{"name":"cup with straw","shortname":":cup_straw:","category":4,"emoji_order":2627},{"name":"beverage box","shortname":":beverage_box:","category":4,"emoji_order":2628,"aliases":[":juice_box:"]},{"name":"mate","shortname":":mate:","category":4,"emoji_order":2629,"aliases":[":yerba_mate:"]},{"name":"ice","shortname":":ice:","category":4,"emoji_order":2630},{"name":"chopsticks","shortname":":chopsticks:","category":4,"emoji_order":2631},{"name":"fork and knife with plate","shortname":":fork_knife_plate:","category":4,"emoji_order":2633},{"name":"fork and knife","shortname":":utensils:","category":4,"emoji_order":2634},{"name":"spoon","shortname":":spoon:","category":4,"emoji_order":2635},{"name":"kitchen knife","shortname":":knife:","category":4,"emoji_order":2636},{"name":"amphora","shortname":":amphora:","category":4,"emoji_order":2637},{"name":"globe showing Europe-Africa","shortname":":earth_africa:","category":5,"emoji_order":2638},{"name":"globe showing Americas","shortname":":earth_americas:","category":5,"emoji_order":2639},{"name":"globe showing Asia-Australia","shortname":":earth_asia:","category":5,"emoji_order":2640},{"name":"globe with meridians","shortname":":globe:","category":5,"emoji_order":2641},{"name":"world map","shortname":":map:","category":5,"emoji_order":2643},{"name":"map of Japan","shortname":":japan:","category":5,"emoji_order":2644},{"name":"compass","shortname":":compass:","category":5,"emoji_order":2645},{"name":"snow-capped mountain","shortname":":snowy_mountain:","category":5,"emoji_order":2647},{"name":"mountain","shortname":":mountain:","category":5,"emoji_order":2649},{"name":"volcano","shortname":":volcano:","category":5,"emoji_order":2650},{"name":"mount fuji","shortname":":mount_fuji:","category":5,"emoji_order":2651},{"name":"camping","shortname":":camping:","category":5,"emoji_order":2653},{"name":"beach with umbrella","shortname":":beach:","category":5,"emoji_order":2655},{"name":"desert","shortname":":desert:","category":5,"emoji_order":2657},{"name":"desert island","shortname":":island:","category":5,"emoji_order":2659},{"name":"national park","shortname":":park:","category":5,"emoji_order":2661},{"name":"stadium","shortname":":stadium:","category":5,"emoji_order":2663},{"name":"classical building","shortname":":classical_building:","category":5,"emoji_order":2665},{"name":"building construction","shortname":":construction_site:","category":5,"emoji_order":2667},{"name":"brick","shortname":":brick:","category":5,"emoji_order":2668},{"name":"houses","shortname":":homes:","category":5,"emoji_order":2670},{"name":"derelict house","shortname":":house_abandoned:","category":5,"emoji_order":2672},{"name":"house","shortname":":house:","category":5,"emoji_order":2673},{"name":"house with garden","shortname":":house_garden:","category":5,"emoji_order":2674},{"name":"office building","shortname":":office:","category":5,"emoji_order":2675},{"name":"Japanese post office","shortname":":ja_post_office:","category":5,"emoji_order":2676},{"name":"post office","shortname":":post_office:","category":5,"emoji_order":2677},{"name":"hospital","shortname":":hospital:","category":5,"emoji_order":2678},{"name":"bank","shortname":":bank:","category":5,"emoji_order":2679},{"name":"hotel","shortname":":hotel:","category":5,"emoji_order":2680},{"name":"love hotel","shortname":":love_hotel:","category":5,"emoji_order":2681},{"name":"convenience store","shortname":":convenience_store:","category":5,"emoji_order":2682},{"name":"school","shortname":":school:","category":5,"emoji_order":2683},{"name":"department store","shortname":":department_store:","category":5,"emoji_order":2684},{"name":"factory","shortname":":factory:","category":5,"emoji_order":2685},{"name":"Japanese castle","shortname":":japanese_castle:","category":5,"emoji_order":2686},{"name":"castle","shortname":":castle:","category":5,"emoji_order":2687,"aliases":[":european_castle:"]},{"name":"wedding","shortname":":wedding:","category":5,"emoji_order":2688},{"name":"Tokyo tower","shortname":":tokyo_tower:","category":5,"emoji_order":2689},{"name":"Statue of Liberty","shortname":":statue_of_liberty:","category":5,"emoji_order":2690},{"name":"church","shortname":":church:","category":5,"emoji_order":2691},{"name":"mosque","shortname":":mosque:","category":5,"emoji_order":2692},{"name":"hindu temple","shortname":":hindu_temple:","category":5,"emoji_order":2693},{"name":"synagogue","shortname":":synagogue:","category":5,"emoji_order":2694},{"name":"shinto shrine","shortname":":shinto_shrine:","category":5,"emoji_order":2696},{"name":"kaaba","shortname":":kaaba:","category":5,"emoji_order":2697},{"name":"fountain","shortname":":fountain:","category":5,"emoji_order":2698},{"name":"tent","shortname":":tent:","category":5,"emoji_order":2699},{"name":"foggy","shortname":":foggy:","category":5,"emoji_order":2700},{"name":"night with stars","shortname":":night_stars:","category":5,"emoji_order":2701},{"name":"cityscape","shortname":":cityscape:","category":5,"emoji_order":2703},{"name":"sunrise over mountains","shortname":":sunrise_over_mountains:","category":5,"emoji_order":2704},{"name":"sunrise","shortname":":sunrise:","category":5,"emoji_order":2705},{"name":"cityscape at dusk","shortname":":dusk:","category":5,"emoji_order":2706},{"name":"sunset","shortname":":sunset:","category":5,"emoji_order":2707},{"name":"bridge at night","shortname":":bridge_at_night:","category":5,"emoji_order":2708},{"name":"hot springs","shortname":":hotsprings:","category":5,"emoji_order":2710},{"name":"carousel horse","shortname":":carousel_horse:","category":5,"emoji_order":2711},{"name":"ferris wheel","shortname":":ferris_wheel:","category":5,"emoji_order":2712},{"name":"roller coaster","shortname":":roller_coaster:","category":5,"emoji_order":2713},{"name":"barber pole","shortname":":barber:","category":5,"emoji_order":2714},{"name":"circus tent","shortname":":circus_tent:","category":5,"emoji_order":2715},{"name":"locomotive","shortname":":steam_locomotive:","category":5,"emoji_order":2716},{"name":"railway car","shortname":":railway_car:","category":5,"emoji_order":2717},{"name":"high-speed train","shortname":":bullettrain_side:","category":5,"emoji_order":2718},{"name":"bullet train","shortname":":bullettrain:","category":5,"emoji_order":2719},{"name":"train","shortname":":train:","category":5,"emoji_order":2720},{"name":"metro","shortname":":metro:","category":5,"emoji_order":2721},{"name":"light rail","shortname":":light_rail:","category":5,"emoji_order":2722},{"name":"station","shortname":":station:","category":5,"emoji_order":2723},{"name":"tram","shortname":":tram:","category":5,"emoji_order":2724},{"name":"monorail","shortname":":monorail:","category":5,"emoji_order":2725},{"name":"mountain railway","shortname":":mountain_railway:","category":5,"emoji_order":2726},{"name":"tram car","shortname":":tram_car:","category":5,"emoji_order":2727},{"name":"bus","shortname":":bus:","category":5,"emoji_order":2728},{"name":"oncoming bus","shortname":":oncoming_bus:","category":5,"emoji_order":2729},{"name":"trolleybus","shortname":":trolleybus:","category":5,"emoji_order":2730},{"name":"minibus","shortname":":minibus:","category":5,"emoji_order":2731},{"name":"ambulance","shortname":":ambulance:","category":5,"emoji_order":2732},{"name":"fire engine","shortname":":fire_engine:","category":5,"emoji_order":2733},{"name":"police car","shortname":":police_car:","category":5,"emoji_order":2734},{"name":"oncoming police car","shortname":":oncoming_police_car:","category":5,"emoji_order":2735},{"name":"taxi","shortname":":taxi:","category":5,"emoji_order":2736},{"name":"oncoming taxi","shortname":":oncoming_taxi:","category":5,"emoji_order":2737},{"name":"automobile","shortname":":red_car:","category":5,"emoji_order":2738},{"name":"oncoming automobile","shortname":":oncoming_automobile:","category":5,"emoji_order":2739},{"name":"sport utility vehicle","shortname":":blue_car:","category":5,"emoji_order":2740},{"name":"delivery truck","shortname":":truck:","category":5,"emoji_order":2741},{"name":"articulated lorry","shortname":":lorry:","category":5,"emoji_order":2742},{"name":"tractor","shortname":":tractor:","category":5,"emoji_order":2743},{"name":"racing car","shortname":":race_car:","category":5,"emoji_order":2745},{"name":"motorcycle","shortname":":motorcycle:","category":5,"emoji_order":2747},{"name":"motor scooter","shortname":":motor_scooter:","category":5,"emoji_order":2748},{"name":"manual wheelchair","shortname":":wheelchair:","category":5,"emoji_order":2749},{"name":"motorized wheelchair","shortname":":motor_wheelchair:","category":5,"emoji_order":2750},{"name":"auto rickshaw","shortname":":auto_rickshaw:","category":5,"emoji_order":2751},{"name":"bicycle","shortname":":bike:","category":5,"emoji_order":2752},{"name":"kick scooter","shortname":":scooter:","category":5,"emoji_order":2753},{"name":"skateboard","shortname":":skateboard:","category":5,"emoji_order":2754},{"name":"bus stop","shortname":":bus_stop:","category":5,"emoji_order":2755},{"name":"motorway","shortname":":motorway:","category":5,"emoji_order":2757},{"name":"railway track","shortname":":railway_track:","category":5,"emoji_order":2759},{"name":"oil drum","shortname":":oil_drum:","category":5,"emoji_order":2761},{"name":"fuel pump","shortname":":fuel_pump:","category":5,"emoji_order":2762},{"name":"police car light","shortname":":rotating_light:","category":5,"emoji_order":2763,"aliases":[":police_light:"]},{"name":"horizontal traffic light","shortname":":traffic_light:","category":5,"emoji_order":2764},{"name":"vertical traffic light","shortname":":vertical_traffic_light:","category":5,"emoji_order":2765},{"name":"stop sign","shortname":":stop_sign:","category":5,"emoji_order":2766,"aliases":[":octagonal_sign:"]},{"name":"construction","shortname":":construction:","category":5,"emoji_order":2767},{"name":"anchor","shortname":":anchor:","category":5,"emoji_order":2768},{"name":"sailboat","shortname":":sailboat:","category":5,"emoji_order":2769},{"name":"canoe","shortname":":canoe:","category":5,"emoji_order":2770},{"name":"speedboat","shortname":":speedboat:","category":5,"emoji_order":2771},{"name":"passenger ship","shortname":":cruise_ship:","category":5,"emoji_order":2773},{"name":"ferry","shortname":":ferry:","category":5,"emoji_order":2775},{"name":"motor boat","shortname":":motorboat:","category":5,"emoji_order":2777},{"name":"ship","shortname":":ship:","category":5,"emoji_order":2778},{"name":"airplane","shortname":":airplane:","category":5,"emoji_order":2780},{"name":"small airplane","shortname":":small_airplane:","category":5,"emoji_order":2782},{"name":"airplane departure","shortname":":airplane_departure:","category":5,"emoji_order":2783},{"name":"airplane arrival","shortname":":airplane_arriving:","category":5,"emoji_order":2784},{"name":"parachute","shortname":":parachute:","category":5,"emoji_order":2785},{"name":"seat","shortname":":seat:","category":5,"emoji_order":2786},{"name":"helicopter","shortname":":helicopter:","category":5,"emoji_order":2787},{"name":"suspension railway","shortname":":suspension_railway:","category":5,"emoji_order":2788},{"name":"mountain cableway","shortname":":mountain_cableway:","category":5,"emoji_order":2789},{"name":"aerial tramway","shortname":":aerial_tramway:","category":5,"emoji_order":2790},{"name":"satellite","shortname":":satellite:","category":5,"emoji_order":2792},{"name":"rocket","shortname":":rocket:","category":5,"emoji_order":2793},{"name":"flying saucer","shortname":":flying_saucer:","category":5,"emoji_order":2794},{"name":"bellhop bell","shortname":":bellhop:","category":5,"emoji_order":2796},{"name":"luggage","shortname":":luggage:","category":5,"emoji_order":2797},{"name":"hourglass done","shortname":":hourglass:","category":5,"emoji_order":2798},{"name":"hourglass not done","shortname":":hourglass_flowing:","category":5,"emoji_order":2799},{"name":"watch","shortname":":watch:","category":5,"emoji_order":2800},{"name":"alarm clock","shortname":":alarm_clock:","category":5,"emoji_order":2801},{"name":"stopwatch","shortname":":stopwatch:","category":5,"emoji_order":2803},{"name":"timer clock","shortname":":timer:","category":5,"emoji_order":2805},{"name":"mantelpiece clock","shortname":":clock:","category":5,"emoji_order":2807},{"name":"twelve o’clock","shortname":":clock12:","category":5,"emoji_order":2808},{"name":"twelve-thirty","shortname":":clock1230:","category":5,"emoji_order":2809},{"name":"one o’clock","shortname":":clock1:","category":5,"emoji_order":2810},{"name":"one-thirty","shortname":":clock130:","category":5,"emoji_order":2811},{"name":"two o’clock","shortname":":clock2:","category":5,"emoji_order":2812},{"name":"two-thirty","shortname":":clock230:","category":5,"emoji_order":2813},{"name":"three o’clock","shortname":":clock3:","category":5,"emoji_order":2814},{"name":"three-thirty","shortname":":clock330:","category":5,"emoji_order":2815},{"name":"four o’clock","shortname":":clock4:","category":5,"emoji_order":2816},{"name":"four-thirty","shortname":":clock430:","category":5,"emoji_order":2817},{"name":"five o’clock","shortname":":clock5:","category":5,"emoji_order":2818},{"name":"five-thirty","shortname":":clock530:","category":5,"emoji_order":2819},{"name":"six o’clock","shortname":":clock6:","category":5,"emoji_order":2820},{"name":"six-thirty","shortname":":clock630:","category":5,"emoji_order":2821},{"name":"seven o’clock","shortname":":clock7:","category":5,"emoji_order":2822},{"name":"seven-thirty","shortname":":clock730:","category":5,"emoji_order":2823},{"name":"eight o’clock","shortname":":clock8:","category":5,"emoji_order":2824},{"name":"eight-thirty","shortname":":clock830:","category":5,"emoji_order":2825},{"name":"nine o’clock","shortname":":clock9:","category":5,"emoji_order":2826},{"name":"nine-thirty","shortname":":clock930:","category":5,"emoji_order":2827},{"name":"ten o’clock","shortname":":clock10:","category":5,"emoji_order":2828},{"name":"ten-thirty","shortname":":clock1030:","category":5,"emoji_order":2829},{"name":"eleven o’clock","shortname":":clock11:","category":5,"emoji_order":2830},{"name":"eleven-thirty","shortname":":clock1130:","category":5,"emoji_order":2831},{"name":"new moon","shortname":":new_moon:","category":5,"emoji_order":2832},{"name":"waxing crescent moon","shortname":":waxing_crescent_moon:","category":5,"emoji_order":2833},{"name":"first quarter moon","shortname":":first_quarter_moon:","category":5,"emoji_order":2834},{"name":"waxing gibbous moon","shortname":":waxing_gibbous_moon:","category":5,"emoji_order":2835},{"name":"full moon","shortname":":full_moon:","category":5,"emoji_order":2836},{"name":"waning gibbous moon","shortname":":waning_gibbous_moon:","category":5,"emoji_order":2837},{"name":"last quarter moon","shortname":":last_quarter_moon:","category":5,"emoji_order":2838},{"name":"waning crescent moon","shortname":":waning_crescent_moon:","category":5,"emoji_order":2839},{"name":"crescent moon","shortname":":crescent_moon:","category":5,"emoji_order":2840},{"name":"new moon face","shortname":":new_moon_face:","category":5,"emoji_order":2841},{"name":"first quarter moon face","shortname":":first_quarter_moon_face:","category":5,"emoji_order":2842},{"name":"last quarter moon face","shortname":":last_quarter_moon_face:","category":5,"emoji_order":2843},{"name":"thermometer","shortname":":thermometer:","category":5,"emoji_order":2845},{"name":"sun","shortname":":sun:","category":5,"emoji_order":2847},{"name":"full moon face","shortname":":full_moon_face:","category":5,"emoji_order":2848},{"name":"sun with face","shortname":":sun_face:","category":5,"emoji_order":2849},{"name":"ringed planet","shortname":":ringed_planet:","category":5,"emoji_order":2850,"aliases":[":saturn:"]},{"name":"star","shortname":":star:","category":5,"emoji_order":2851},{"name":"glowing star","shortname":":star2:","category":5,"emoji_order":2852,"aliases":[":glowing_star:"]},{"name":"shooting star","shortname":":star3:","category":5,"emoji_order":2853,"aliases":[":shooting_star:"]},{"name":"milky way","shortname":":milky_way:","category":5,"emoji_order":2854},{"name":"cloud","shortname":":cloud:","category":5,"emoji_order":2856},{"name":"sun behind cloud","shortname":":partly_sunny:","category":5,"emoji_order":2857},{"name":"cloud with lightning and rain","shortname":":storm:","category":5,"emoji_order":2859},{"name":"sun behind small cloud","shortname":":overcast:","category":5,"emoji_order":2861},{"name":"sun behind large cloud","shortname":":cloudy:","category":5,"emoji_order":2863},{"name":"sun behind rain cloud","shortname":":sunshower:","category":5,"emoji_order":2865},{"name":"cloud with rain","shortname":":rain:","category":5,"emoji_order":2867},{"name":"cloud with snow","shortname":":snow:","category":5,"emoji_order":2869},{"name":"cloud with lightning","shortname":":lightning:","category":5,"emoji_order":2871},{"name":"tornado","shortname":":tornado:","category":5,"emoji_order":2873},{"name":"fog","shortname":":fog:","category":5,"emoji_order":2875},{"name":"wind face","shortname":":wind_face:","category":5,"emoji_order":2877},{"name":"cyclone","shortname":":cyclone:","category":5,"emoji_order":2878},{"name":"rainbow","shortname":":rainbow:","category":5,"emoji_order":2879},{"name":"closed umbrella","shortname":":closed_umbrella:","category":5,"emoji_order":2880},{"name":"umbrella","shortname":":umbrella:","category":5,"emoji_order":2882},{"name":"umbrella with rain drops","shortname":":umbrella_rain:","category":5,"emoji_order":2883},{"name":"umbrella on ground","shortname":":beach_umbrella:","category":5,"emoji_order":2885},{"name":"high voltage","shortname":":zap:","category":5,"emoji_order":2886,"aliases":[":high_voltage:"]},{"name":"snowflake","shortname":":snowflake:","category":5,"emoji_order":2888},{"name":"snowman","shortname":":snowy_snowman:","category":5,"emoji_order":2890},{"name":"snowman without snow","shortname":":snowman:","category":5,"emoji_order":2891},{"name":"comet","shortname":":comet:","category":5,"emoji_order":2893},{"name":"fire","shortname":":fire:","category":5,"emoji_order":2894},{"name":"droplet","shortname":":droplet:","category":5,"emoji_order":2895},{"name":"water wave","shortname":":ocean:","category":5,"emoji_order":2896},{"name":"jack-o-lantern","shortname":":jack_o_lantern:","category":6,"emoji_order":2897},{"name":"Christmas tree","shortname":":christmas_tree:","category":6,"emoji_order":2898,"aliases":[":xmas_tree:"]},{"name":"fireworks","shortname":":fireworks:","category":6,"emoji_order":2899},{"name":"sparkler","shortname":":sparkler:","category":6,"emoji_order":2900},{"name":"firecracker","shortname":":firecracker:","category":6,"emoji_order":2901},{"name":"sparkles","shortname":":sparkles:","category":6,"emoji_order":2902},{"name":"balloon","shortname":":balloon:","category":6,"emoji_order":2903},{"name":"party popper","shortname":":tada:","category":6,"emoji_order":2904,"aliases":[":party:"]},{"name":"confetti ball","shortname":":confetti_ball:","category":6,"emoji_order":2905},{"name":"tanabata tree","shortname":":tanabata_tree:","category":6,"emoji_order":2906},{"name":"pine decoration","shortname":":bamboo:","category":6,"emoji_order":2907,"aliases":[":pine_decor:"]},{"name":"Japanese dolls","shortname":":dolls:","category":6,"emoji_order":2908},{"name":"carp streamer","shortname":":carp_streamer:","category":6,"emoji_order":2909},{"name":"wind chime","shortname":":wind_chime:","category":6,"emoji_order":2910},{"name":"moon viewing ceremony","shortname":":moon_ceremony:","category":6,"emoji_order":2911,"aliases":[":rice_scene:"]},{"name":"red envelope","shortname":":red_envelope:","category":6,"emoji_order":2912},{"name":"ribbon","shortname":":ribbon:","category":6,"emoji_order":2913},{"name":"wrapped gift","shortname":":gift:","category":6,"emoji_order":2914},{"name":"reminder ribbon","shortname":":reminder_ribbon:","category":6,"emoji_order":2916},{"name":"admission tickets","shortname":":tickets:","category":6,"emoji_order":2918,"aliases":[":admission:"]},{"name":"ticket","shortname":":ticket:","category":6,"emoji_order":2919},{"name":"military medal","shortname":":military_medal:","category":6,"emoji_order":2921},{"name":"trophy","shortname":":trophy:","category":6,"emoji_order":2922},{"name":"sports medal","shortname":":medal:","category":6,"emoji_order":2923},{"name":"1st place medal","shortname":":first_place:","category":6,"emoji_order":2924},{"name":"2nd place medal","shortname":":second_place:","category":6,"emoji_order":2925},{"name":"3rd place medal","shortname":":third_place:","category":6,"emoji_order":2926},{"name":"soccer ball","shortname":":soccer:","category":6,"emoji_order":2927},{"name":"baseball","shortname":":baseball:","category":6,"emoji_order":2928},{"name":"softball","shortname":":softball:","category":6,"emoji_order":2929},{"name":"basketball","shortname":":basketball:","category":6,"emoji_order":2930},{"name":"volleyball","shortname":":volleyball:","category":6,"emoji_order":2931},{"name":"american football","shortname":":football:","category":6,"emoji_order":2932},{"name":"rugby football","shortname":":rugby:","category":6,"emoji_order":2933},{"name":"tennis","shortname":":tennis:","category":6,"emoji_order":2934},{"name":"flying disc","shortname":":flying_disc:","category":6,"emoji_order":2935},{"name":"bowling","shortname":":bowling:","category":6,"emoji_order":2936},{"name":"cricket game","shortname":":cricket_game:","category":6,"emoji_order":2937},{"name":"field hockey","shortname":":field_hockey:","category":6,"emoji_order":2938},{"name":"ice hockey","shortname":":hockey:","category":6,"emoji_order":2939},{"name":"lacrosse","shortname":":lacrosse:","category":6,"emoji_order":2940},{"name":"ping pong","shortname":":ping_pong:","category":6,"emoji_order":2941},{"name":"badminton","shortname":":badminton:","category":6,"emoji_order":2942},{"name":"boxing glove","shortname":":boxing_glove:","category":6,"emoji_order":2943},{"name":"martial arts uniform","shortname":":gi:","category":6,"emoji_order":2944,"aliases":[":martial_arts_uniform:"]},{"name":"goal net","shortname":":goal:","category":6,"emoji_order":2945},{"name":"flag in hole","shortname":":golf:","category":6,"emoji_order":2946},{"name":"ice skate","shortname":":ice_skate:","category":6,"emoji_order":2948},{"name":"fishing pole","shortname":":fishing_pole:","category":6,"emoji_order":2949},{"name":"diving mask","shortname":":diving_mask:","category":6,"emoji_order":2950,"aliases":[":scuba_mask:"]},{"name":"running shirt","shortname":":running_shirt:","category":6,"emoji_order":2951},{"name":"skis","shortname":":ski:","category":6,"emoji_order":2952},{"name":"sled","shortname":":sled:","category":6,"emoji_order":2953},{"name":"curling stone","shortname":":curling_stone:","category":6,"emoji_order":2954},{"name":"direct hit","shortname":":dart:","category":6,"emoji_order":2955},{"name":"yo-yo","shortname":":yoyo:","category":6,"emoji_order":2956},{"name":"kite","shortname":":kite:","category":6,"emoji_order":2957},{"name":"pool 8 ball","shortname":":8ball:","category":6,"emoji_order":2958},{"name":"crystal ball","shortname":":crystal_ball:","category":6,"emoji_order":2959},{"name":"nazar amulet","shortname":":nazar_amulet:","category":6,"emoji_order":2960},{"name":"video game","shortname":":video_game:","category":6,"emoji_order":2961},{"name":"joystick","shortname":":joystick:","category":6,"emoji_order":2963},{"name":"slot machine","shortname":":slot_machine:","category":6,"emoji_order":2964},{"name":"game die","shortname":":game_die:","category":6,"emoji_order":2965},{"name":"puzzle piece","shortname":":jigsaw:","category":6,"emoji_order":2966,"aliases":[":puzzle_piece:"]},{"name":"teddy bear","shortname":":teddy_bear:","category":6,"emoji_order":2967},{"name":"spade suit","shortname":":spades:","category":6,"emoji_order":2969},{"name":"heart suit","shortname":":hearts:","category":6,"emoji_order":2971},{"name":"diamond suit","shortname":":diamonds:","category":6,"emoji_order":2973},{"name":"club suit","shortname":":clubs:","category":6,"emoji_order":2975},{"name":"chess pawn","shortname":":chess_pawn:","category":6,"emoji_order":2977},{"name":"joker","shortname":":black_joker:","category":6,"emoji_order":2978},{"name":"mahjong red dragon","shortname":":mahjong:","category":6,"emoji_order":2979},{"name":"flower playing cards","shortname":":flower_cards:","category":6,"emoji_order":2980},{"name":"performing arts","shortname":":performing_arts:","category":6,"emoji_order":2981},{"name":"framed picture","shortname":":frame_photo:","category":6,"emoji_order":2983},{"name":"artist palette","shortname":":art:","category":6,"emoji_order":2984,"aliases":[":palette:"]},{"name":"thread","shortname":":spool:","category":6,"emoji_order":2985},{"name":"yarn","shortname":":yarn:","category":6,"emoji_order":2986},{"name":"glasses","shortname":":glasses:","category":7,"emoji_order":2987},{"name":"sunglasses","shortname":":sunglasses:","category":7,"emoji_order":2989},{"name":"goggles","shortname":":goggles:","category":7,"emoji_order":2990},{"name":"lab coat","shortname":":lab_coat:","category":7,"emoji_order":2991},{"name":"safety vest","shortname":":safety_vest:","category":7,"emoji_order":2992},{"name":"necktie","shortname":":necktie:","category":7,"emoji_order":2993,"aliases":[":tie:"]},{"name":"t-shirt","shortname":":shirt:","category":7,"emoji_order":2994},{"name":"jeans","shortname":":jeans:","category":7,"emoji_order":2995},{"name":"scarf","shortname":":scarf:","category":7,"emoji_order":2996},{"name":"gloves","shortname":":gloves:","category":7,"emoji_order":2997},{"name":"coat","shortname":":coat:","category":7,"emoji_order":2998},{"name":"socks","shortname":":socks:","category":7,"emoji_order":2999},{"name":"dress","shortname":":dress:","category":7,"emoji_order":3000},{"name":"kimono","shortname":":kimono:","category":7,"emoji_order":3001},{"name":"sari","shortname":":sari:","category":7,"emoji_order":3002},{"name":"one-piece swimsuit","shortname":":one_piece_swimsuit:","category":7,"emoji_order":3003},{"name":"briefs","shortname":":briefs:","category":7,"emoji_order":3004},{"name":"shorts","shortname":":shorts:","category":7,"emoji_order":3005},{"name":"bikini","shortname":":bikini:","category":7,"emoji_order":3006},{"name":"woman’s clothes","shortname":":blouse:","category":7,"emoji_order":3007,"aliases":[":womans_clothes:"]},{"name":"purse","shortname":":purse:","category":7,"emoji_order":3008},{"name":"handbag","shortname":":handbag:","category":7,"emoji_order":3009},{"name":"clutch bag","shortname":":pouch:","category":7,"emoji_order":3010,"aliases":[":clutch_bag:"]},{"name":"shopping bags","shortname":":shopping_bags:","category":7,"emoji_order":3012},{"name":"backpack","shortname":":backpack:","category":7,"emoji_order":3013},{"name":"man’s shoe","shortname":":dress_shoe:","category":7,"emoji_order":3014,"aliases":[":mans_shoe:"]},{"name":"running shoe","shortname":":sneaker:","category":7,"emoji_order":3015,"aliases":[":athletic_shoe:"]},{"name":"hiking boot","shortname":":hiking_boot:","category":7,"emoji_order":3016},{"name":"flat shoe","shortname":":flat_shoe:","category":7,"emoji_order":3017},{"name":"high-heeled shoe","shortname":":high_heel:","category":7,"emoji_order":3018},{"name":"woman’s sandal","shortname":":womans_sandal:","category":7,"emoji_order":3019},{"name":"ballet shoes","shortname":":ballet_shoes:","category":7,"emoji_order":3020},{"name":"woman’s boot","shortname":":womans_boot:","category":7,"emoji_order":3021},{"name":"crown","shortname":":crown:","category":7,"emoji_order":3022},{"name":"woman’s hat","shortname":":womans_hat:","category":7,"emoji_order":3023},{"name":"top hat","shortname":":top_hat:","category":7,"emoji_order":3024},{"name":"graduation cap","shortname":":graduation_cap:","category":7,"emoji_order":3025},{"name":"billed cap","shortname":":billed_cap:","category":7,"emoji_order":3026},{"name":"rescue worker’s helmet","shortname":":helmet_cross:","category":7,"emoji_order":3028},{"name":"prayer beads","shortname":":prayer_beads:","category":7,"emoji_order":3029},{"name":"lipstick","shortname":":lipstick:","category":7,"emoji_order":3030},{"name":"ring","shortname":":ring:","category":7,"emoji_order":3031},{"name":"gem stone","shortname":":gem:","category":7,"emoji_order":3032},{"name":"muted speaker","shortname":":mute:","category":7,"emoji_order":3033,"aliases":[":no_sound:"]},{"name":"speaker low volume","shortname":":speaker:","category":7,"emoji_order":3034,"aliases":[":low_sound:"]},{"name":"speaker medium volume","shortname":":sound:","category":7,"emoji_order":3035},{"name":"speaker high volume","shortname":":loud_sound:","category":7,"emoji_order":3036},{"name":"loudspeaker","shortname":":loudspeaker:","category":7,"emoji_order":3037},{"name":"megaphone","shortname":":megaphone:","category":7,"emoji_order":3038},{"name":"postal horn","shortname":":postal_horn:","category":7,"emoji_order":3039},{"name":"bell","shortname":":bell:","category":7,"emoji_order":3040},{"name":"bell with slash","shortname":":no_bell:","category":7,"emoji_order":3041},{"name":"musical score","shortname":":musical_score:","category":7,"emoji_order":3042},{"name":"musical note","shortname":":musical_note:","category":7,"emoji_order":3043},{"name":"musical notes","shortname":":musical_notes:","category":7,"emoji_order":3044},{"name":"studio microphone","shortname":":studio_microphone:","category":7,"emoji_order":3046},{"name":"level slider","shortname":":level_slider:","category":7,"emoji_order":3048},{"name":"control knobs","shortname":":control_knobs:","category":7,"emoji_order":3050},{"name":"microphone","shortname":":microphone:","category":7,"emoji_order":3051},{"name":"headphone","shortname":":headphones:","category":7,"emoji_order":3052},{"name":"radio","shortname":":radio:","category":7,"emoji_order":3053},{"name":"saxophone","shortname":":saxophone:","category":7,"emoji_order":3054},{"name":"guitar","shortname":":guitar:","category":7,"emoji_order":3055},{"name":"musical keyboard","shortname":":musical_keyboard:","category":7,"emoji_order":3056},{"name":"trumpet","shortname":":trumpet:","category":7,"emoji_order":3057},{"name":"violin","shortname":":violin:","category":7,"emoji_order":3058},{"name":"banjo","shortname":":banjo:","category":7,"emoji_order":3059},{"name":"drum","shortname":":drum:","category":7,"emoji_order":3060},{"name":"mobile phone","shortname":":mobile:","category":7,"emoji_order":3061,"aliases":[":iphone:",":android:"]},{"name":"mobile phone with arrow","shortname":":mobile_calling:","category":7,"emoji_order":3062},{"name":"telephone","shortname":":telephone:","category":7,"emoji_order":3064},{"name":"telephone receiver","shortname":":telephone_receiver:","category":7,"emoji_order":3065},{"name":"pager","shortname":":pager:","category":7,"emoji_order":3066},{"name":"fax machine","shortname":":fax:","category":7,"emoji_order":3067},{"name":"battery","shortname":":battery:","category":7,"emoji_order":3068},{"name":"electric plug","shortname":":electric_plug:","category":7,"emoji_order":3069},{"name":"laptop computer","shortname":":laptop:","category":7,"emoji_order":3070},{"name":"desktop computer","shortname":":desktop:","category":7,"emoji_order":3072,"aliases":[":computer:"]},{"name":"printer","shortname":":printer:","category":7,"emoji_order":3074},{"name":"keyboard","shortname":":keyboard:","category":7,"emoji_order":3076},{"name":"computer mouse","shortname":":computer_mouse:","category":7,"emoji_order":3078},{"name":"trackball","shortname":":trackball:","category":7,"emoji_order":3080},{"name":"computer disk","shortname":":minidisc:","category":7,"emoji_order":3081},{"name":"floppy disk","shortname":":floppy_disk:","category":7,"emoji_order":3082},{"name":"optical disk","shortname":":cd:","category":7,"emoji_order":3083,"aliases":[":disk:"]},{"name":"dvd","shortname":":dvd:","category":7,"emoji_order":3084},{"name":"abacus","shortname":":abacus:","category":7,"emoji_order":3085},{"name":"movie camera","shortname":":movie_camera:","category":7,"emoji_order":3086},{"name":"film frames","shortname":":film_frames:","category":7,"emoji_order":3088},{"name":"film projector","shortname":":projector:","category":7,"emoji_order":3090},{"name":"clapper board","shortname":":clapper:","category":7,"emoji_order":3091},{"name":"television","shortname":":tv:","category":7,"emoji_order":3092},{"name":"camera","shortname":":camera:","category":7,"emoji_order":3093},{"name":"camera with flash","shortname":":camera_flash:","category":7,"emoji_order":3094},{"name":"video camera","shortname":":video_camera:","category":7,"emoji_order":3095},{"name":"videocassette","shortname":":vhs:","category":7,"emoji_order":3096},{"name":"magnifying glass tilted left","shortname":":mag:","category":7,"emoji_order":3097},{"name":"magnifying glass tilted right","shortname":":mag_right:","category":7,"emoji_order":3098},{"name":"candle","shortname":":candle:","category":7,"emoji_order":3100},{"name":"light bulb","shortname":":bulb:","category":7,"emoji_order":3101,"aliases":[":light_bulb:"]},{"name":"flashlight","shortname":":flashlight:","category":7,"emoji_order":3102},{"name":"red paper lantern","shortname":":red_lantern:","category":7,"emoji_order":3103},{"name":"diya lamp","shortname":":diya_lamp:","category":7,"emoji_order":3104},{"name":"notebook with decorative cover","shortname":":decorative_notebook:","category":7,"emoji_order":3105},{"name":"closed book","shortname":":closed_book:","category":7,"emoji_order":3106},{"name":"open book","shortname":":book:","category":7,"emoji_order":3107},{"name":"green book","shortname":":green_book:","category":7,"emoji_order":3108},{"name":"blue book","shortname":":blue_book:","category":7,"emoji_order":3109},{"name":"orange book","shortname":":orange_book:","category":7,"emoji_order":3110},{"name":"books","shortname":":books:","category":7,"emoji_order":3111},{"name":"notebook","shortname":":notebook:","category":7,"emoji_order":3112},{"name":"ledger","shortname":":ledger:","category":7,"emoji_order":3113},{"name":"page with curl","shortname":":page_curl:","category":7,"emoji_order":3114},{"name":"scroll","shortname":":scroll:","category":7,"emoji_order":3115},{"name":"page facing up","shortname":":page_facing_up:","category":7,"emoji_order":3116},{"name":"newspaper","shortname":":newspaper:","category":7,"emoji_order":3117},{"name":"rolled-up newspaper","shortname":":rolled_newspaper:","category":7,"emoji_order":3119},{"name":"bookmark tabs","shortname":":bookmark_tabs:","category":7,"emoji_order":3120},{"name":"bookmark","shortname":":bookmark:","category":7,"emoji_order":3121},{"name":"label","shortname":":label:","category":7,"emoji_order":3123},{"name":"money bag","shortname":":moneybag:","category":7,"emoji_order":3124},{"name":"yen banknote","shortname":":yen:","category":7,"emoji_order":3125},{"name":"dollar banknote","shortname":":dollar:","category":7,"emoji_order":3126},{"name":"euro banknote","shortname":":euro:","category":7,"emoji_order":3127},{"name":"pound banknote","shortname":":pound:","category":7,"emoji_order":3128},{"name":"money with wings","shortname":":money_wings:","category":7,"emoji_order":3129},{"name":"credit card","shortname":":credit_card:","category":7,"emoji_order":3130},{"name":"receipt","shortname":":receipt:","category":7,"emoji_order":3131},{"name":"chart increasing with yen","shortname":":ja_chart:","category":7,"emoji_order":3132},{"name":"currency exchange","shortname":":currency_exchange:","category":7,"emoji_order":3133},{"name":"heavy dollar sign","shortname":":dollar_sign:","category":7,"emoji_order":3134},{"name":"envelope","shortname":":envelope:","category":7,"emoji_order":3136},{"name":"e-mail","shortname":":email:","category":7,"emoji_order":3137},{"name":"incoming envelope","shortname":":incoming_envelope:","category":7,"emoji_order":3138},{"name":"envelope with arrow","shortname":":envelope_arrow:","category":7,"emoji_order":3139},{"name":"outbox tray","shortname":":outbox_tray:","category":7,"emoji_order":3140},{"name":"inbox tray","shortname":":inbox_tray:","category":7,"emoji_order":3141},{"name":"package","shortname":":package:","category":7,"emoji_order":3142},{"name":"closed mailbox with raised flag","shortname":":mailbox:","category":7,"emoji_order":3143},{"name":"closed mailbox with lowered flag","shortname":":mailbox_closed:","category":7,"emoji_order":3144},{"name":"open mailbox with raised flag","shortname":":mailbox_mail:","category":7,"emoji_order":3145},{"name":"open mailbox with lowered flag","shortname":":mailbox_no_mail:","category":7,"emoji_order":3146},{"name":"postbox","shortname":":postbox:","category":7,"emoji_order":3147},{"name":"ballot box with ballot","shortname":":ballot_box:","category":7,"emoji_order":3149},{"name":"pencil","shortname":":pencil:","category":7,"emoji_order":3151},{"name":"black nib","shortname":":black_nib:","category":7,"emoji_order":3153},{"name":"fountain pen","shortname":":fountain_pen:","category":7,"emoji_order":3155},{"name":"pen","shortname":":pen:","category":7,"emoji_order":3157},{"name":"paintbrush","shortname":":paintbrush:","category":7,"emoji_order":3159},{"name":"crayon","shortname":":crayon:","category":7,"emoji_order":3161},{"name":"memo","shortname":":memo:","category":7,"emoji_order":3162},{"name":"briefcase","shortname":":briefcase:","category":7,"emoji_order":3163},{"name":"file folder","shortname":":file_folder:","category":7,"emoji_order":3164},{"name":"open file folder","shortname":":open_file_folder:","category":7,"emoji_order":3165},{"name":"card index dividers","shortname":":dividers:","category":7,"emoji_order":3167},{"name":"calendar","shortname":":date:","category":7,"emoji_order":3168,"aliases":[":calendar:"]},{"name":"tear-off calendar","shortname":":torn_calendar:","category":7,"emoji_order":3169},{"name":"spiral notepad","shortname":":notepad_spiral:","category":7,"emoji_order":3171},{"name":"spiral calendar","shortname":":calendar_spiral:","category":7,"emoji_order":3173},{"name":"card index","shortname":":card_index:","category":7,"emoji_order":3174},{"name":"chart increasing","shortname":":chart_up:","category":7,"emoji_order":3175},{"name":"chart decreasing","shortname":":chart_down:","category":7,"emoji_order":3176},{"name":"bar chart","shortname":":bar_chart:","category":7,"emoji_order":3177},{"name":"clipboard","shortname":":clipboard:","category":7,"emoji_order":3178},{"name":"pushpin","shortname":":pushpin:","category":7,"emoji_order":3179},{"name":"round pushpin","shortname":":round_pushpin:","category":7,"emoji_order":3180},{"name":"paperclip","shortname":":paperclip:","category":7,"emoji_order":3181},{"name":"linked paperclips","shortname":":paperclips:","category":7,"emoji_order":3183},{"name":"straight ruler","shortname":":straight_ruler:","category":7,"emoji_order":3184},{"name":"triangular ruler","shortname":":triangular_ruler:","category":7,"emoji_order":3185},{"name":"scissors","shortname":":scissors:","category":7,"emoji_order":3187},{"name":"card file box","shortname":":card_box:","category":7,"emoji_order":3189},{"name":"file cabinet","shortname":":file_cabinet:","category":7,"emoji_order":3191},{"name":"wastebasket","shortname":":trashcan:","category":7,"emoji_order":3193,"aliases":[":wastebasket:"]},{"name":"locked","shortname":":lock:","category":7,"emoji_order":3194},{"name":"unlocked","shortname":":unlock:","category":7,"emoji_order":3195},{"name":"locked with pen","shortname":":locked_pen:","category":7,"emoji_order":3196},{"name":"locked with key","shortname":":locked_key:","category":7,"emoji_order":3197},{"name":"key","shortname":":key:","category":7,"emoji_order":3198},{"name":"old key","shortname":":old_key:","category":7,"emoji_order":3200},{"name":"hammer","shortname":":hammer:","category":7,"emoji_order":3201},{"name":"axe","shortname":":axe:","category":7,"emoji_order":3202},{"name":"pick","shortname":":pick:","category":7,"emoji_order":3204},{"name":"hammer and pick","shortname":":hammer_pick:","category":7,"emoji_order":3206},{"name":"hammer and wrench","shortname":":tools:","category":7,"emoji_order":3208,"aliases":[":hammer_wrench:"]},{"name":"dagger","shortname":":dagger:","category":7,"emoji_order":3210},{"name":"crossed swords","shortname":":crossed_swords:","category":7,"emoji_order":3212},{"name":"pistol","shortname":":gun:","category":7,"emoji_order":3213,"aliases":[":pistol:"]},{"name":"bow and arrow","shortname":":bow:","category":7,"emoji_order":3214},{"name":"shield","shortname":":shield:","category":7,"emoji_order":3216},{"name":"wrench","shortname":":wrench:","category":7,"emoji_order":3217},{"name":"nut and bolt","shortname":":nut_and_bolt:","category":7,"emoji_order":3218},{"name":"gear","shortname":":gear:","category":7,"emoji_order":3220},{"name":"clamp","shortname":":clamp:","category":7,"emoji_order":3222,"aliases":[":compression:"]},{"name":"balance scale","shortname":":scales:","category":7,"emoji_order":3224},{"name":"probing cane","shortname":":probing_cane:","category":7,"emoji_order":3225},{"name":"link","shortname":":link:","category":7,"emoji_order":3226},{"name":"chains","shortname":":chains:","category":7,"emoji_order":3228},{"name":"toolbox","shortname":":toolbox:","category":7,"emoji_order":3229},{"name":"magnet","shortname":":magnet:","category":7,"emoji_order":3230},{"name":"alembic","shortname":":alembic:","category":7,"emoji_order":3232},{"name":"test tube","shortname":":test_tube:","category":7,"emoji_order":3233},{"name":"petri dish","shortname":":petri_dish:","category":7,"emoji_order":3234},{"name":"dna","shortname":":dna:","category":7,"emoji_order":3235,"aliases":[":double_helix:"]},{"name":"microscope","shortname":":microscope:","category":7,"emoji_order":3236},{"name":"telescope","shortname":":telescope:","category":7,"emoji_order":3237},{"name":"satellite antenna","shortname":":satellite_antenna:","category":7,"emoji_order":3238},{"name":"syringe","shortname":":syringe:","category":7,"emoji_order":3239},{"name":"drop of blood","shortname":":blood_drop:","category":7,"emoji_order":3240},{"name":"pill","shortname":":pill:","category":7,"emoji_order":3241},{"name":"adhesive bandage","shortname":":bandaid:","category":7,"emoji_order":3242,"aliases":[":adhesive_bandage:"]},{"name":"stethoscope","shortname":":stethoscope:","category":7,"emoji_order":3243},{"name":"door","shortname":":door:","category":7,"emoji_order":3244},{"name":"bed","shortname":":bed:","category":7,"emoji_order":3246},{"name":"couch and lamp","shortname":":couch:","category":7,"emoji_order":3248},{"name":"chair","shortname":":chair:","category":7,"emoji_order":3249},{"name":"toilet","shortname":":toilet:","category":7,"emoji_order":3250},{"name":"shower","shortname":":shower:","category":7,"emoji_order":3251},{"name":"bathtub","shortname":":bathtub:","category":7,"emoji_order":3252},{"name":"razor","shortname":":razor:","category":7,"emoji_order":3253},{"name":"lotion bottle","shortname":":lotion:","category":7,"emoji_order":3254},{"name":"safety pin","shortname":":safety_pin:","category":7,"emoji_order":3255},{"name":"broom","shortname":":broom:","category":7,"emoji_order":3256},{"name":"basket","shortname":":basket:","category":7,"emoji_order":3257},{"name":"roll of paper","shortname":":toilet_paper:","category":7,"emoji_order":3258},{"name":"soap","shortname":":soap:","category":7,"emoji_order":3259},{"name":"sponge","shortname":":sponge:","category":7,"emoji_order":3260},{"name":"fire extinguisher","shortname":":fire_extinguisher:","category":7,"emoji_order":3261},{"name":"shopping cart","shortname":":shopping_cart:","category":7,"emoji_order":3262},{"name":"cigarette","shortname":":cigarette:","category":7,"emoji_order":3263,"aliases":[":smoking:"]},{"name":"coffin","shortname":":coffin:","category":7,"emoji_order":3265},{"name":"funeral urn","shortname":":urn:","category":7,"emoji_order":3267},{"name":"moai","shortname":":moai:","category":7,"emoji_order":3268},{"name":"ATM sign","shortname":":atm:","category":8,"emoji_order":3269},{"name":"litter in bin sign","shortname":":litter_bin:","category":8,"emoji_order":3270},{"name":"potable water","shortname":":potable_water:","category":8,"emoji_order":3271},{"name":"wheelchair symbol","shortname":":handicapped:","category":8,"emoji_order":3272},{"name":"men’s room","shortname":":mens:","category":8,"emoji_order":3273},{"name":"women’s room","shortname":":womens:","category":8,"emoji_order":3274},{"name":"restroom","shortname":":restroom:","category":8,"emoji_order":3275,"aliases":[":bathroom:"]},{"name":"baby symbol","shortname":":baby_symbol:","category":8,"emoji_order":3276},{"name":"water closet","shortname":":wc:","category":8,"emoji_order":3277},{"name":"passport control","shortname":":passport_control:","category":8,"emoji_order":3278},{"name":"customs","shortname":":customs:","category":8,"emoji_order":3279},{"name":"baggage claim","shortname":":baggage_claim:","category":8,"emoji_order":3280},{"name":"left luggage","shortname":":left_luggage:","category":8,"emoji_order":3281},{"name":"warning","shortname":":warning:","category":8,"emoji_order":3283},{"name":"children crossing","shortname":":children_crossing:","category":8,"emoji_order":3284},{"name":"no entry","shortname":":no_entry:","category":8,"emoji_order":3285},{"name":"prohibited","shortname":":no_entry_sign:","category":8,"emoji_order":3286},{"name":"no bicycles","shortname":":no_bicycles:","category":8,"emoji_order":3287},{"name":"no smoking","shortname":":no_smoking:","category":8,"emoji_order":3288},{"name":"no littering","shortname":":do_not_litter:","category":8,"emoji_order":3289},{"name":"non-potable water","shortname":":non_potable_water:","category":8,"emoji_order":3290},{"name":"no pedestrians","shortname":":no_pedestrians:","category":8,"emoji_order":3291},{"name":"no mobile phones","shortname":":no_mobile_phones:","category":8,"emoji_order":3292},{"name":"no one under eighteen","shortname":":underage:","category":8,"emoji_order":3293},{"name":"radioactive","shortname":":radioactive:","category":8,"emoji_order":3295},{"name":"biohazard","shortname":":biohazard:","category":8,"emoji_order":3297},{"name":"up arrow","shortname":":arrow_up:","category":8,"emoji_order":3299},{"name":"up-right arrow","shortname":":arrow_upper_right:","category":8,"emoji_order":3301},{"name":"right arrow","shortname":":arrow_right:","category":8,"emoji_order":3303},{"name":"down-right arrow","shortname":":arrow_lower_right:","category":8,"emoji_order":3305},{"name":"down arrow","shortname":":arrow_down:","category":8,"emoji_order":3307},{"name":"down-left arrow","shortname":":arrow_lower_left:","category":8,"emoji_order":3309},{"name":"left arrow","shortname":":arrow_left:","category":8,"emoji_order":3311},{"name":"up-left arrow","shortname":":arrow_upper_left:","category":8,"emoji_order":3313},{"name":"up-down arrow","shortname":":arrow_up_down:","category":8,"emoji_order":3315},{"name":"left-right arrow","shortname":":arrow_left_right:","category":8,"emoji_order":3317},{"name":"right arrow curving left","shortname":":arrow_left_hook:","category":8,"emoji_order":3319},{"name":"left arrow curving right","shortname":":arrow_right_hook:","category":8,"emoji_order":3321},{"name":"right arrow curving up","shortname":":arrow_heading_up:","category":8,"emoji_order":3323},{"name":"right arrow curving down","shortname":":arrow_heading_down:","category":8,"emoji_order":3325},{"name":"clockwise vertical arrows","shortname":":clockwise:","category":8,"emoji_order":3326},{"name":"counterclockwise arrows button","shortname":":counter_clockwise:","category":8,"emoji_order":3327},{"name":"BACK arrow","shortname":":back:","category":8,"emoji_order":3328},{"name":"END arrow","shortname":":end:","category":8,"emoji_order":3329},{"name":"ON! arrow","shortname":":on:","category":8,"emoji_order":3330},{"name":"SOON arrow","shortname":":soon:","category":8,"emoji_order":3331},{"name":"TOP arrow","shortname":":top:","category":8,"emoji_order":3332},{"name":"place of worship","shortname":":place_of_worship:","category":8,"emoji_order":3333},{"name":"atom symbol","shortname":":atom:","category":8,"emoji_order":3335},{"name":"om","shortname":":om_symbol:","category":8,"emoji_order":3337},{"name":"star of David","shortname":":star_of_david:","category":8,"emoji_order":3339},{"name":"wheel of dharma","shortname":":wheel_of_dharma:","category":8,"emoji_order":3341},{"name":"yin yang","shortname":":yin_yang:","category":8,"emoji_order":3343},{"name":"latin cross","shortname":":cross:","category":8,"emoji_order":3345},{"name":"orthodox cross","shortname":":orthodox_cross:","category":8,"emoji_order":3347},{"name":"star and crescent","shortname":":star_and_crescent:","category":8,"emoji_order":3349},{"name":"peace symbol","shortname":":peace:","category":8,"emoji_order":3351},{"name":"menorah","shortname":":menorah:","category":8,"emoji_order":3352},{"name":"dotted six-pointed star","shortname":":six_pointed_star:","category":8,"emoji_order":3353},{"name":"Aries","shortname":":aries:","category":8,"emoji_order":3354},{"name":"Taurus","shortname":":taurus:","category":8,"emoji_order":3355},{"name":"Gemini","shortname":":gemini:","category":8,"emoji_order":3356},{"name":"Cancer","shortname":":cancer:","category":8,"emoji_order":3357},{"name":"Leo","shortname":":leo:","category":8,"emoji_order":3358},{"name":"Virgo","shortname":":virgo:","category":8,"emoji_order":3359},{"name":"Libra","shortname":":libra:","category":8,"emoji_order":3360},{"name":"Scorpio","shortname":":scorpius:","category":8,"emoji_order":3361},{"name":"Sagittarius","shortname":":sagittarius:","category":8,"emoji_order":3362},{"name":"Capricorn","shortname":":capricorn:","category":8,"emoji_order":3363},{"name":"Aquarius","shortname":":aquarius:","category":8,"emoji_order":3364},{"name":"Pisces","shortname":":pisces:","category":8,"emoji_order":3365},{"name":"Ophiuchus","shortname":":ophiuchus:","category":8,"emoji_order":3366},{"name":"shuffle tracks button","shortname":":shuffle:","category":8,"emoji_order":3367},{"name":"repeat button","shortname":":repeat:","category":8,"emoji_order":3368},{"name":"repeat single button","shortname":":repeat_single:","category":8,"emoji_order":3369},{"name":"play button","shortname":":play:","category":8,"emoji_order":3371},{"name":"fast-forward button","shortname":":fast_forward:","category":8,"emoji_order":3372},{"name":"next track button","shortname":":next_track:","category":8,"emoji_order":3374},{"name":"play or pause button","shortname":":play_pause:","category":8,"emoji_order":3376},{"name":"reverse button","shortname":":reverse:","category":8,"emoji_order":3378},{"name":"fast reverse button","shortname":":rewind:","category":8,"emoji_order":3379},{"name":"last track button","shortname":":previous_track:","category":8,"emoji_order":3381},{"name":"upwards button","shortname":":up_button:","category":8,"emoji_order":3382},{"name":"fast up button","shortname":":fast_up_button:","category":8,"emoji_order":3383},{"name":"downwards button","shortname":":down_button:","category":8,"emoji_order":3384},{"name":"fast down button","shortname":":fast_down_button:","category":8,"emoji_order":3385},{"name":"pause button","shortname":":pause:","category":8,"emoji_order":3387},{"name":"stop button","shortname":":stop:","category":8,"emoji_order":3389},{"name":"record button","shortname":":record:","category":8,"emoji_order":3391},{"name":"eject button","shortname":":eject:","category":8,"emoji_order":3393},{"name":"cinema","shortname":":cinema:","category":8,"emoji_order":3394},{"name":"dim button","shortname":":dim:","category":8,"emoji_order":3395,"aliases":[":low_brightness:"]},{"name":"bright button","shortname":":bright:","category":8,"emoji_order":3396,"aliases":[":high_brightness:"]},{"name":"antenna bars","shortname":":signal_strength:","category":8,"emoji_order":3397,"aliases":[":antenna_bars:"]},{"name":"vibration mode","shortname":":vibration_mode:","category":8,"emoji_order":3398},{"name":"mobile phone off","shortname":":mobile_phone_off:","category":8,"emoji_order":3399},{"name":"female sign","shortname":":female:","category":8,"emoji_order":3401,"aliases":[":female_sign:"]},{"name":"male sign","shortname":":male:","category":8,"emoji_order":3403,"aliases":[":male_sign:"]},{"name":"medical symbol","shortname":":medical:","category":8,"emoji_order":3405},{"name":"infinity","shortname":":infinity:","category":8,"emoji_order":3407},{"name":"recycling symbol","shortname":":recycle:","category":8,"emoji_order":3409},{"name":"fleur-de-lis","shortname":":fleur-de-lis:","category":8,"emoji_order":3411},{"name":"trident emblem","shortname":":trident:","category":8,"emoji_order":3412},{"name":"name badge","shortname":":name_badge:","category":8,"emoji_order":3413},{"name":"Japanese symbol for beginner","shortname":":ja_beginner:","category":8,"emoji_order":3414},{"name":"hollow red circle","shortname":":o:","category":8,"emoji_order":3415},{"name":"check mark button","shortname":":white_check_mark:","category":8,"emoji_order":3416},{"name":"check box with check","shortname":":checked_ballot:","category":8,"emoji_order":3418},{"name":"check mark","shortname":":check_mark:","category":8,"emoji_order":3420},{"name":"multiplication sign","shortname":":multiplication:","category":8,"emoji_order":3422},{"name":"cross mark","shortname":":x:","category":8,"emoji_order":3423,"aliases":[":cross_mark:"]},{"name":"cross mark button","shortname":":cross_mark_button:","category":8,"emoji_order":3424},{"name":"plus sign","shortname":":plus:","category":8,"emoji_order":3425},{"name":"minus sign","shortname":":minus:","category":8,"emoji_order":3426},{"name":"division sign","shortname":":division:","category":8,"emoji_order":3427},{"name":"curly loop","shortname":":curly_loop:","category":8,"emoji_order":3428},{"name":"double curly loop","shortname":":double_curly_loop:","category":8,"emoji_order":3429},{"name":"part alternation mark","shortname":":part_alternation_mark:","category":8,"emoji_order":3431},{"name":"eight-spoked asterisk","shortname":":eight_spoked_asterisk:","category":8,"emoji_order":3433},{"name":"eight-pointed star","shortname":":eight_pointed_star:","category":8,"emoji_order":3435},{"name":"sparkle","shortname":":sparkle:","category":8,"emoji_order":3437},{"name":"double exclamation mark","shortname":":bangbang:","category":8,"emoji_order":3439,"aliases":[":double_exclamation:"]},{"name":"exclamation question mark","shortname":":interrobang:","category":8,"emoji_order":3441,"aliases":[":exclamation_question:"]},{"name":"question mark","shortname":":question:","category":8,"emoji_order":3442},{"name":"white question mark","shortname":":white_question:","category":8,"emoji_order":3443},{"name":"white exclamation mark","shortname":":white_exclamation:","category":8,"emoji_order":3444},{"name":"exclamation mark","shortname":":exclamation:","category":8,"emoji_order":3445},{"name":"wavy dash","shortname":":wavy_dash:","category":8,"emoji_order":3447},{"name":"copyright","shortname":":copyright:","category":8,"emoji_order":3449},{"name":"registered","shortname":":registered:","category":8,"emoji_order":3451},{"name":"trade mark","shortname":":tm:","category":8,"emoji_order":3453},{"name":"keycap: #","shortname":":hash:","category":8,"emoji_order":3454},{"name":"keycap: *","shortname":":asterisk:","category":8,"emoji_order":3456},{"name":"keycap: 0","shortname":":zero:","category":8,"emoji_order":3458},{"name":"keycap: 1","shortname":":one:","category":8,"emoji_order":3460},{"name":"keycap: 2","shortname":":two:","category":8,"emoji_order":3462},{"name":"keycap: 3","shortname":":three:","category":8,"emoji_order":3464},{"name":"keycap: 4","shortname":":four:","category":8,"emoji_order":3466},{"name":"keycap: 5","shortname":":five:","category":8,"emoji_order":3468},{"name":"keycap: 6","shortname":":six:","category":8,"emoji_order":3470},{"name":"keycap: 7","shortname":":seven:","category":8,"emoji_order":3472},{"name":"keycap: 8","shortname":":eight:","category":8,"emoji_order":3474},{"name":"keycap: 9","shortname":":nine:","category":8,"emoji_order":3476},{"name":"keycap: 10","shortname":":ten:","category":8,"emoji_order":3478},{"name":"input latin uppercase","shortname":":upper_abcd:","category":8,"emoji_order":3479},{"name":"input latin lowercase","shortname":":abcd:","category":8,"emoji_order":3480},{"name":"input numbers","shortname":":1234:","category":8,"emoji_order":3481},{"name":"input symbols","shortname":":symbols:","category":8,"emoji_order":3482},{"name":"input latin letters","shortname":":abc:","category":8,"emoji_order":3483},{"name":"A button (blood type)","shortname":":a_blood:","category":8,"emoji_order":3485},{"name":"AB button (blood type)","shortname":":ab_blood:","category":8,"emoji_order":3486},{"name":"B button (blood type)","shortname":":b_blood:","category":8,"emoji_order":3488},{"name":"CL button","shortname":":cl:","category":8,"emoji_order":3489},{"name":"COOL button","shortname":":cool:","category":8,"emoji_order":3490},{"name":"FREE button","shortname":":free:","category":8,"emoji_order":3491},{"name":"information","shortname":":info:","category":8,"emoji_order":3493},{"name":"ID button","shortname":":id:","category":8,"emoji_order":3494},{"name":"circled M","shortname":":m:","category":8,"emoji_order":3496},{"name":"NEW button","shortname":":new:","category":8,"emoji_order":3497},{"name":"NG button","shortname":":ng:","category":8,"emoji_order":3498},{"name":"O button (blood type)","shortname":":o_blood:","category":8,"emoji_order":3500},{"name":"OK button","shortname":":ok:","category":8,"emoji_order":3501},{"name":"P button","shortname":":p:","category":8,"emoji_order":3503},{"name":"SOS button","shortname":":sos:","category":8,"emoji_order":3504},{"name":"UP! button","shortname":":up:","category":8,"emoji_order":3505},{"name":"VS button","shortname":":vs:","category":8,"emoji_order":3506},{"name":"Japanese “here” button","shortname":":ja_here:","category":8,"emoji_order":3507,"aliases":[":koko:"]},{"name":"Japanese “service charge” button","shortname":":ja_service_charge:","category":8,"emoji_order":3509},{"name":"Japanese “monthly amount” button","shortname":":ja_monthly_amount:","category":8,"emoji_order":3511},{"name":"Japanese “not free of charge” button","shortname":":ja_not_free_of_carge:","category":8,"emoji_order":3512},{"name":"Japanese “reserved” button","shortname":":ja_reserved:","category":8,"emoji_order":3513},{"name":"Japanese “bargain” button","shortname":":ja_bargain:","category":8,"emoji_order":3514},{"name":"Japanese “discount” button","shortname":":ja_discount:","category":8,"emoji_order":3515},{"name":"Japanese “free of charge” button","shortname":":ja_free_of_charge:","category":8,"emoji_order":3516},{"name":"Japanese “prohibited” button","shortname":":ja_prohibited:","category":8,"emoji_order":3517},{"name":"Japanese “acceptable” button","shortname":":ja_acceptable:","category":8,"emoji_order":3518},{"name":"Japanese “application” button","shortname":":ja_application:","category":8,"emoji_order":3519},{"name":"Japanese “passing grade” button","shortname":":ja_passing_grade:","category":8,"emoji_order":3520},{"name":"Japanese “vacancy” button","shortname":":ja_vacancy:","category":8,"emoji_order":3521},{"name":"Japanese “congratulations” button","shortname":":ja_congratulations:","category":8,"emoji_order":3523},{"name":"Japanese “secret” button","shortname":":ja_secret:","category":8,"emoji_order":3525},{"name":"Japanese “open for business” button","shortname":":ja_open_for_business:","category":8,"emoji_order":3526},{"name":"Japanese “no vacancy” button","shortname":":ja_no_vacancy:","category":8,"emoji_order":3527},{"name":"red circle","shortname":":red_circle:","category":8,"emoji_order":3528},{"name":"orange circle","shortname":":orange_circle:","category":8,"emoji_order":3529},{"name":"yellow circle","shortname":":yellow_circle:","category":8,"emoji_order":3530},{"name":"green circle","shortname":":green_circle:","category":8,"emoji_order":3531},{"name":"blue circle","shortname":":blue_circle:","category":8,"emoji_order":3532},{"name":"purple circle","shortname":":purple_circle:","category":8,"emoji_order":3533},{"name":"brown circle","shortname":":brown_circle:","category":8,"emoji_order":3534},{"name":"black circle","shortname":":black_circle:","category":8,"emoji_order":3535},{"name":"white circle","shortname":":white_circle:","category":8,"emoji_order":3536},{"name":"red square","shortname":":red_square:","category":8,"emoji_order":3537},{"name":"orange square","shortname":":orange_square:","category":8,"emoji_order":3538},{"name":"yellow square","shortname":":yellow_square:","category":8,"emoji_order":3539},{"name":"green square","shortname":":green_square:","category":8,"emoji_order":3540},{"name":"blue square","shortname":":blue_square:","category":8,"emoji_order":3541},{"name":"purple square","shortname":":purple_square:","category":8,"emoji_order":3542},{"name":"brown square","shortname":":brown_square:","category":8,"emoji_order":3543},{"name":"black large square","shortname":":large_black_square:","category":8,"emoji_order":3544},{"name":"white large square","shortname":":large_white_square:","category":8,"emoji_order":3545},{"name":"black medium square","shortname":":medium_black_square:","category":8,"emoji_order":3547},{"name":"white medium square","shortname":":medium_white_square:","category":8,"emoji_order":3549},{"name":"black medium-small square","shortname":":medium_small_black_square:","category":8,"emoji_order":3550},{"name":"white medium-small square","shortname":":medium_small_white_square:","category":8,"emoji_order":3551},{"name":"black small square","shortname":":small_black_square:","category":8,"emoji_order":3553},{"name":"white small square","shortname":":small_white_square:","category":8,"emoji_order":3555},{"name":"large orange diamond","shortname":":large_orange_diamond:","category":8,"emoji_order":3556},{"name":"large blue diamond","shortname":":large_blue_diamond:","category":8,"emoji_order":3557},{"name":"small orange diamond","shortname":":small_orange_diamond:","category":8,"emoji_order":3558},{"name":"small blue diamond","shortname":":small_blue_diamond:","category":8,"emoji_order":3559},{"name":"red triangle pointed up","shortname":":up_red_triangle:","category":8,"emoji_order":3560},{"name":"red triangle pointed down","shortname":":down_red_triangle:","category":8,"emoji_order":3561},{"name":"diamond with a dot","shortname":":diamond_dot:","category":8,"emoji_order":3562},{"name":"radio button","shortname":":radio_button:","category":8,"emoji_order":3563},{"name":"white square button","shortname":":white_square_button:","category":8,"emoji_order":3564},{"name":"black square button","shortname":":black_square_button:","category":8,"emoji_order":3565},{"name":"chequered flag","shortname":":checkered_flag:","category":9,"emoji_order":3566},{"name":"triangular flag","shortname":":triangle_flag:","category":9,"emoji_order":3567},{"name":"crossed flags","shortname":":crossed_flags:","category":9,"emoji_order":3568},{"name":"black flag","shortname":":black_flag:","category":9,"emoji_order":3569},{"name":"white flag","shortname":":white_flag:","category":9,"emoji_order":3571},{"name":"rainbow flag","shortname":":rainbow_flag:","category":9,"emoji_order":3572},{"name":"pirate flag","shortname":":pirate_flag:","category":9,"emoji_order":3574,"aliases":[":jolly_roger:"]},{"name":"flag: Ascension Island","shortname":":flag_ac:","category":9,"emoji_order":3576},{"name":"flag: Andorra","shortname":":flag_ad:","category":9,"emoji_order":3577},{"name":"flag: United Arab Emirates","shortname":":flag_ae:","category":9,"emoji_order":3578},{"name":"flag: Afghanistan","shortname":":flag_af:","category":9,"emoji_order":3579},{"name":"flag: Antigua & Barbuda","shortname":":flag_ag:","category":9,"emoji_order":3580},{"name":"flag: Anguilla","shortname":":flag_ai:","category":9,"emoji_order":3581},{"name":"flag: Albania","shortname":":flag_al:","category":9,"emoji_order":3582},{"name":"flag: Armenia","shortname":":flag_am:","category":9,"emoji_order":3583},{"name":"flag: Angola","shortname":":flag_ao:","category":9,"emoji_order":3584},{"name":"flag: Antarctica","shortname":":flag_aq:","category":9,"emoji_order":3585},{"name":"flag: Argentina","shortname":":flag_ar:","category":9,"emoji_order":3586},{"name":"flag: American Samoa","shortname":":flag_as:","category":9,"emoji_order":3587},{"name":"flag: Austria","shortname":":flag_at:","category":9,"emoji_order":3588},{"name":"flag: Australia","shortname":":flag_au:","category":9,"emoji_order":3589},{"name":"flag: Aruba","shortname":":flag_aw:","category":9,"emoji_order":3590},{"name":"flag: Åland Islands","shortname":":flag_ax:","category":9,"emoji_order":3591},{"name":"flag: Azerbaijan","shortname":":flag_az:","category":9,"emoji_order":3592},{"name":"flag: Bosnia & Herzegovina","shortname":":flag_ba:","category":9,"emoji_order":3593},{"name":"flag: Barbados","shortname":":flag_bb:","category":9,"emoji_order":3594},{"name":"flag: Bangladesh","shortname":":flag_bd:","category":9,"emoji_order":3595},{"name":"flag: Belgium","shortname":":flag_be:","category":9,"emoji_order":3596},{"name":"flag: Burkina Faso","shortname":":flag_bf:","category":9,"emoji_order":3597},{"name":"flag: Bulgaria","shortname":":flag_bg:","category":9,"emoji_order":3598},{"name":"flag: Bahrain","shortname":":flag_bh:","category":9,"emoji_order":3599},{"name":"flag: Burundi","shortname":":flag_bi:","category":9,"emoji_order":3600},{"name":"flag: Benin","shortname":":flag_bj:","category":9,"emoji_order":3601},{"name":"flag: St. Barthélemy","shortname":":flag_bl:","category":9,"emoji_order":3602},{"name":"flag: Bermuda","shortname":":flag_bm:","category":9,"emoji_order":3603},{"name":"flag: Brunei","shortname":":flag_bn:","category":9,"emoji_order":3604},{"name":"flag: Bolivia","shortname":":flag_bo:","category":9,"emoji_order":3605},{"name":"flag: Caribbean Netherlands","shortname":":flag_bq:","category":9,"emoji_order":3606},{"name":"flag: Brazil","shortname":":flag_br:","category":9,"emoji_order":3607},{"name":"flag: Bahamas","shortname":":flag_bs:","category":9,"emoji_order":3608},{"name":"flag: Bhutan","shortname":":flag_bt:","category":9,"emoji_order":3609},{"name":"flag: Bouvet Island","shortname":":flag_bv:","category":9,"emoji_order":3610},{"name":"flag: Botswana","shortname":":flag_bw:","category":9,"emoji_order":3611},{"name":"flag: Belarus","shortname":":flag_by:","category":9,"emoji_order":3612},{"name":"flag: Belize","shortname":":flag_bz:","category":9,"emoji_order":3613},{"name":"flag: Canada","shortname":":flag_ca:","category":9,"emoji_order":3614},{"name":"flag: Cocos (Keeling) Islands","shortname":":flag_cc:","category":9,"emoji_order":3615},{"name":"flag: Congo - Kinshasa","shortname":":flag_cd:","category":9,"emoji_order":3616},{"name":"flag: Central African Republic","shortname":":flag_cf:","category":9,"emoji_order":3617},{"name":"flag: Congo - Brazzaville","shortname":":flag_cg:","category":9,"emoji_order":3618},{"name":"flag: Switzerland","shortname":":flag_ch:","category":9,"emoji_order":3619},{"name":"flag: Côte d’Ivoire","shortname":":flag_ci:","category":9,"emoji_order":3620},{"name":"flag: Cook Islands","shortname":":flag_ck:","category":9,"emoji_order":3621},{"name":"flag: Chile","shortname":":flag_cl:","category":9,"emoji_order":3622},{"name":"flag: Cameroon","shortname":":flag_cm:","category":9,"emoji_order":3623},{"name":"flag: China","shortname":":flag_cn:","category":9,"emoji_order":3624},{"name":"flag: Colombia","shortname":":flag_co:","category":9,"emoji_order":3625},{"name":"flag: Clipperton Island","shortname":":flag_cp:","category":9,"emoji_order":3626},{"name":"flag: Costa Rica","shortname":":flag_cr:","category":9,"emoji_order":3627},{"name":"flag: Cuba","shortname":":flag_cu:","category":9,"emoji_order":3628},{"name":"flag: Cape Verde","shortname":":flag_cv:","category":9,"emoji_order":3629},{"name":"flag: Curaçao","shortname":":flag_cw:","category":9,"emoji_order":3630},{"name":"flag: Christmas Island","shortname":":flag_cx:","category":9,"emoji_order":3631},{"name":"flag: Cyprus","shortname":":flag_cy:","category":9,"emoji_order":3632},{"name":"flag: Czechia","shortname":":flag_cz:","category":9,"emoji_order":3633},{"name":"flag: Germany","shortname":":flag_de:","category":9,"emoji_order":3634},{"name":"flag: Diego Garcia","shortname":":flag_dg:","category":9,"emoji_order":3635},{"name":"flag: Djibouti","shortname":":flag_dj:","category":9,"emoji_order":3636},{"name":"flag: Denmark","shortname":":flag_dk:","category":9,"emoji_order":3637},{"name":"flag: Dominica","shortname":":flag_dm:","category":9,"emoji_order":3638},{"name":"flag: Dominican Republic","shortname":":flag_do:","category":9,"emoji_order":3639},{"name":"flag: Algeria","shortname":":flag_dz:","category":9,"emoji_order":3640},{"name":"flag: Ceuta & Melilla","shortname":":flag_ea:","category":9,"emoji_order":3641},{"name":"flag: Ecuador","shortname":":flag_ec:","category":9,"emoji_order":3642},{"name":"flag: Estonia","shortname":":flag_ee:","category":9,"emoji_order":3643},{"name":"flag: Egypt","shortname":":flag_eg:","category":9,"emoji_order":3644},{"name":"flag: Western Sahara","shortname":":flag_eh:","category":9,"emoji_order":3645},{"name":"flag: Eritrea","shortname":":flag_er:","category":9,"emoji_order":3646},{"name":"flag: Spain","shortname":":flag_es:","category":9,"emoji_order":3647},{"name":"flag: Ethiopia","shortname":":flag_et:","category":9,"emoji_order":3648},{"name":"flag: European Union","shortname":":flag_eu:","category":9,"emoji_order":3649},{"name":"flag: Finland","shortname":":flag_fi:","category":9,"emoji_order":3650},{"name":"flag: Fiji","shortname":":flag_fj:","category":9,"emoji_order":3651},{"name":"flag: Falkland Islands","shortname":":flag_fk:","category":9,"emoji_order":3652},{"name":"flag: Micronesia","shortname":":flag_fm:","category":9,"emoji_order":3653},{"name":"flag: Faroe Islands","shortname":":flag_fo:","category":9,"emoji_order":3654},{"name":"flag: France","shortname":":flag_fr:","category":9,"emoji_order":3655},{"name":"flag: Gabon","shortname":":flag_ga:","category":9,"emoji_order":3656},{"name":"flag: United Kingdom","shortname":":flag_gb:","category":9,"emoji_order":3657},{"name":"flag: Grenada","shortname":":flag_gd:","category":9,"emoji_order":3658},{"name":"flag: Georgia","shortname":":flag_ge:","category":9,"emoji_order":3659},{"name":"flag: French Guiana","shortname":":flag_gf:","category":9,"emoji_order":3660},{"name":"flag: Guernsey","shortname":":flag_gg:","category":9,"emoji_order":3661},{"name":"flag: Ghana","shortname":":flag_gh:","category":9,"emoji_order":3662},{"name":"flag: Gibraltar","shortname":":flag_gi:","category":9,"emoji_order":3663},{"name":"flag: Greenland","shortname":":flag_gl:","category":9,"emoji_order":3664},{"name":"flag: Gambia","shortname":":flag_gm:","category":9,"emoji_order":3665},{"name":"flag: Guinea","shortname":":flag_gn:","category":9,"emoji_order":3666},{"name":"flag: Guadeloupe","shortname":":flag_gp:","category":9,"emoji_order":3667},{"name":"flag: Equatorial Guinea","shortname":":flag_gq:","category":9,"emoji_order":3668},{"name":"flag: Greece","shortname":":flag_gr:","category":9,"emoji_order":3669},{"name":"flag: South Georgia & South Sandwich Islands","shortname":":flag_gs:","category":9,"emoji_order":3670},{"name":"flag: Guatemala","shortname":":flag_gt:","category":9,"emoji_order":3671},{"name":"flag: Guam","shortname":":flag_gu:","category":9,"emoji_order":3672},{"name":"flag: Guinea-Bissau","shortname":":flag_gw:","category":9,"emoji_order":3673},{"name":"flag: Guyana","shortname":":flag_gy:","category":9,"emoji_order":3674},{"name":"flag: Hong Kong SAR China","shortname":":flag_hk:","category":9,"emoji_order":3675},{"name":"flag: Heard & McDonald Islands","shortname":":flag_hm:","category":9,"emoji_order":3676},{"name":"flag: Honduras","shortname":":flag_hn:","category":9,"emoji_order":3677},{"name":"flag: Croatia","shortname":":flag_hr:","category":9,"emoji_order":3678},{"name":"flag: Haiti","shortname":":flag_ht:","category":9,"emoji_order":3679},{"name":"flag: Hungary","shortname":":flag_hu:","category":9,"emoji_order":3680},{"name":"flag: Canary Islands","shortname":":flag_ic:","category":9,"emoji_order":3681},{"name":"flag: Indonesia","shortname":":flag_id:","category":9,"emoji_order":3682},{"name":"flag: Ireland","shortname":":flag_ie:","category":9,"emoji_order":3683},{"name":"flag: Israel","shortname":":flag_il:","category":9,"emoji_order":3684},{"name":"flag: Isle of Man","shortname":":flag_im:","category":9,"emoji_order":3685},{"name":"flag: India","shortname":":flag_in:","category":9,"emoji_order":3686},{"name":"flag: British Indian Ocean Territory","shortname":":flag_io:","category":9,"emoji_order":3687},{"name":"flag: Iraq","shortname":":flag_iq:","category":9,"emoji_order":3688},{"name":"flag: Iran","shortname":":flag_ir:","category":9,"emoji_order":3689},{"name":"flag: Iceland","shortname":":flag_is:","category":9,"emoji_order":3690},{"name":"flag: Italy","shortname":":flag_it:","category":9,"emoji_order":3691},{"name":"flag: Jersey","shortname":":flag_je:","category":9,"emoji_order":3692},{"name":"flag: Jamaica","shortname":":flag_jm:","category":9,"emoji_order":3693},{"name":"flag: Jordan","shortname":":flag_jo:","category":9,"emoji_order":3694},{"name":"flag: Japan","shortname":":flag_jp:","category":9,"emoji_order":3695},{"name":"flag: Kenya","shortname":":flag_ke:","category":9,"emoji_order":3696},{"name":"flag: Kyrgyzstan","shortname":":flag_kg:","category":9,"emoji_order":3697},{"name":"flag: Cambodia","shortname":":flag_kh:","category":9,"emoji_order":3698},{"name":"flag: Kiribati","shortname":":flag_ki:","category":9,"emoji_order":3699},{"name":"flag: Comoros","shortname":":flag_km:","category":9,"emoji_order":3700},{"name":"flag: St. Kitts & Nevis","shortname":":flag_kn:","category":9,"emoji_order":3701},{"name":"flag: North Korea","shortname":":flag_kp:","category":9,"emoji_order":3702},{"name":"flag: South Korea","shortname":":flag_kr:","category":9,"emoji_order":3703},{"name":"flag: Kuwait","shortname":":flag_kw:","category":9,"emoji_order":3704},{"name":"flag: Cayman Islands","shortname":":flag_ky:","category":9,"emoji_order":3705},{"name":"flag: Kazakhstan","shortname":":flag_kz:","category":9,"emoji_order":3706},{"name":"flag: Laos","shortname":":flag_la:","category":9,"emoji_order":3707},{"name":"flag: Lebanon","shortname":":flag_lb:","category":9,"emoji_order":3708},{"name":"flag: St. Lucia","shortname":":flag_lc:","category":9,"emoji_order":3709},{"name":"flag: Liechtenstein","shortname":":flag_li:","category":9,"emoji_order":3710},{"name":"flag: Sri Lanka","shortname":":flag_lk:","category":9,"emoji_order":3711},{"name":"flag: Liberia","shortname":":flag_lr:","category":9,"emoji_order":3712},{"name":"flag: Lesotho","shortname":":flag_ls:","category":9,"emoji_order":3713},{"name":"flag: Lithuania","shortname":":flag_lt:","category":9,"emoji_order":3714},{"name":"flag: Luxembourg","shortname":":flag_lu:","category":9,"emoji_order":3715},{"name":"flag: Latvia","shortname":":flag_lv:","category":9,"emoji_order":3716},{"name":"flag: Libya","shortname":":flag_ly:","category":9,"emoji_order":3717},{"name":"flag: Morocco","shortname":":flag_ma:","category":9,"emoji_order":3718},{"name":"flag: Monaco","shortname":":flag_mc:","category":9,"emoji_order":3719},{"name":"flag: Moldova","shortname":":flag_md:","category":9,"emoji_order":3720},{"name":"flag: Montenegro","shortname":":flag_me:","category":9,"emoji_order":3721},{"name":"flag: St. Martin","shortname":":flag_mf:","category":9,"emoji_order":3722},{"name":"flag: Madagascar","shortname":":flag_mg:","category":9,"emoji_order":3723},{"name":"flag: Marshall Islands","shortname":":flag_mh:","category":9,"emoji_order":3724},{"name":"flag: North Macedonia","shortname":":flag_mk:","category":9,"emoji_order":3725},{"name":"flag: Mali","shortname":":flag_ml:","category":9,"emoji_order":3726},{"name":"flag: Myanmar (Burma)","shortname":":flag_mm:","category":9,"emoji_order":3727},{"name":"flag: Mongolia","shortname":":flag_mn:","category":9,"emoji_order":3728},{"name":"flag: Macao SAR China","shortname":":flag_mo:","category":9,"emoji_order":3729},{"name":"flag: Northern Mariana Islands","shortname":":flag_mp:","category":9,"emoji_order":3730},{"name":"flag: Martinique","shortname":":flag_mq:","category":9,"emoji_order":3731},{"name":"flag: Mauritania","shortname":":flag_mr:","category":9,"emoji_order":3732},{"name":"flag: Montserrat","shortname":":flag_ms:","category":9,"emoji_order":3733},{"name":"flag: Malta","shortname":":flag_mt:","category":9,"emoji_order":3734},{"name":"flag: Mauritius","shortname":":flag_mu:","category":9,"emoji_order":3735},{"name":"flag: Maldives","shortname":":flag_mv:","category":9,"emoji_order":3736},{"name":"flag: Malawi","shortname":":flag_mw:","category":9,"emoji_order":3737},{"name":"flag: Mexico","shortname":":flag_mx:","category":9,"emoji_order":3738},{"name":"flag: Malaysia","shortname":":flag_my:","category":9,"emoji_order":3739},{"name":"flag: Mozambique","shortname":":flag_mz:","category":9,"emoji_order":3740},{"name":"flag: Namibia","shortname":":flag_na:","category":9,"emoji_order":3741},{"name":"flag: New Caledonia","shortname":":flag_nc:","category":9,"emoji_order":3742},{"name":"flag: Niger","shortname":":flag_ne:","category":9,"emoji_order":3743},{"name":"flag: Norfolk Island","shortname":":flag_nf:","category":9,"emoji_order":3744},{"name":"flag: Nigeria","shortname":":flag_ng:","category":9,"emoji_order":3745},{"name":"flag: Nicaragua","shortname":":flag_ni:","category":9,"emoji_order":3746},{"name":"flag: Netherlands","shortname":":flag_nl:","category":9,"emoji_order":3747},{"name":"flag: Norway","shortname":":flag_no:","category":9,"emoji_order":3748},{"name":"flag: Nepal","shortname":":flag_np:","category":9,"emoji_order":3749},{"name":"flag: Nauru","shortname":":flag_nr:","category":9,"emoji_order":3750},{"name":"flag: Niue","shortname":":flag_nu:","category":9,"emoji_order":3751},{"name":"flag: New Zealand","shortname":":flag_nz:","category":9,"emoji_order":3752},{"name":"flag: Oman","shortname":":flag_om:","category":9,"emoji_order":3753},{"name":"flag: Panama","shortname":":flag_pa:","category":9,"emoji_order":3754},{"name":"flag: Peru","shortname":":flag_pe:","category":9,"emoji_order":3755},{"name":"flag: French Polynesia","shortname":":flag_pf:","category":9,"emoji_order":3756},{"name":"flag: Papua New Guinea","shortname":":flag_pg:","category":9,"emoji_order":3757},{"name":"flag: Philippines","shortname":":flag_ph:","category":9,"emoji_order":3758},{"name":"flag: Pakistan","shortname":":flag_pk:","category":9,"emoji_order":3759},{"name":"flag: Poland","shortname":":flag_pl:","category":9,"emoji_order":3760},{"name":"flag: St. Pierre & Miquelon","shortname":":flag_pm:","category":9,"emoji_order":3761},{"name":"flag: Pitcairn Islands","shortname":":flag_pn:","category":9,"emoji_order":3762},{"name":"flag: Puerto Rico","shortname":":flag_pr:","category":9,"emoji_order":3763},{"name":"flag: Palestinian Territories","shortname":":flag_ps:","category":9,"emoji_order":3764},{"name":"flag: Portugal","shortname":":flag_pt:","category":9,"emoji_order":3765},{"name":"flag: Palau","shortname":":flag_pw:","category":9,"emoji_order":3766},{"name":"flag: Paraguay","shortname":":flag_py:","category":9,"emoji_order":3767},{"name":"flag: Qatar","shortname":":flag_qa:","category":9,"emoji_order":3768},{"name":"flag: Réunion","shortname":":flag_re:","category":9,"emoji_order":3769},{"name":"flag: Romania","shortname":":flag_ro:","category":9,"emoji_order":3770},{"name":"flag: Serbia","shortname":":flag_rs:","category":9,"emoji_order":3771},{"name":"flag: Russia","shortname":":flag_ru:","category":9,"emoji_order":3772},{"name":"flag: Rwanda","shortname":":flag_rw:","category":9,"emoji_order":3773},{"name":"flag: Saudi Arabia","shortname":":flag_sa:","category":9,"emoji_order":3774},{"name":"flag: Solomon Islands","shortname":":flag_sb:","category":9,"emoji_order":3775},{"name":"flag: Seychelles","shortname":":flag_sc:","category":9,"emoji_order":3776},{"name":"flag: Sudan","shortname":":flag_sd:","category":9,"emoji_order":3777},{"name":"flag: Sweden","shortname":":flag_se:","category":9,"emoji_order":3778},{"name":"flag: Singapore","shortname":":flag_sg:","category":9,"emoji_order":3779},{"name":"flag: St. Helena","shortname":":flag_sh:","category":9,"emoji_order":3780},{"name":"flag: Slovenia","shortname":":flag_si:","category":9,"emoji_order":3781},{"name":"flag: Svalbard & Jan Mayen","shortname":":flag_sj:","category":9,"emoji_order":3782},{"name":"flag: Slovakia","shortname":":flag_sk:","category":9,"emoji_order":3783},{"name":"flag: Sierra Leone","shortname":":flag_sl:","category":9,"emoji_order":3784},{"name":"flag: San Marino","shortname":":flag_sm:","category":9,"emoji_order":3785},{"name":"flag: Senegal","shortname":":flag_sn:","category":9,"emoji_order":3786},{"name":"flag: Somalia","shortname":":flag_so:","category":9,"emoji_order":3787},{"name":"flag: Suriname","shortname":":flag_sr:","category":9,"emoji_order":3788},{"name":"flag: South Sudan","shortname":":flag_ss:","category":9,"emoji_order":3789},{"name":"flag: São Tomé & Príncipe","shortname":":flag_st:","category":9,"emoji_order":3790},{"name":"flag: El Salvador","shortname":":flag_sv:","category":9,"emoji_order":3791},{"name":"flag: Sint Maarten","shortname":":flag_sx:","category":9,"emoji_order":3792},{"name":"flag: Syria","shortname":":flag_sy:","category":9,"emoji_order":3793},{"name":"flag: Eswatini","shortname":":flag_sz:","category":9,"emoji_order":3794},{"name":"flag: Tristan da Cunha","shortname":":flag_ta:","category":9,"emoji_order":3795},{"name":"flag: Turks & Caicos Islands","shortname":":flag_tc:","category":9,"emoji_order":3796},{"name":"flag: Chad","shortname":":flag_td:","category":9,"emoji_order":3797},{"name":"flag: French Southern Territories","shortname":":flag_tf:","category":9,"emoji_order":3798},{"name":"flag: Togo","shortname":":flag_tg:","category":9,"emoji_order":3799},{"name":"flag: Thailand","shortname":":flag_th:","category":9,"emoji_order":3800},{"name":"flag: Tajikistan","shortname":":flag_tj:","category":9,"emoji_order":3801},{"name":"flag: Tokelau","shortname":":flag_tk:","category":9,"emoji_order":3802},{"name":"flag: Timor-Leste","shortname":":flag_tl:","category":9,"emoji_order":3803},{"name":"flag: Turkmenistan","shortname":":flag_tm:","category":9,"emoji_order":3804},{"name":"flag: Tunisia","shortname":":flag_tn:","category":9,"emoji_order":3805},{"name":"flag: Tonga","shortname":":flag_to:","category":9,"emoji_order":3806},{"name":"flag: Turkey","shortname":":flag_tr:","category":9,"emoji_order":3807},{"name":"flag: Trinidad & Tobago","shortname":":flag_tt:","category":9,"emoji_order":3808},{"name":"flag: Tuvalu","shortname":":flag_tv:","category":9,"emoji_order":3809},{"name":"flag: Taiwan","shortname":":flag_tw:","category":9,"emoji_order":3810},{"name":"flag: Tanzania","shortname":":flag_tz:","category":9,"emoji_order":3811},{"name":"flag: Ukraine","shortname":":flag_ua:","category":9,"emoji_order":3812},{"name":"flag: Uganda","shortname":":flag_ug:","category":9,"emoji_order":3813},{"name":"flag: U.S. Outlying Islands","shortname":":flag_um:","category":9,"emoji_order":3814},{"name":"flag: United Nations","shortname":":flag_un:","category":9,"emoji_order":3815},{"name":"flag: United States","shortname":":flag_us:","category":9,"emoji_order":3816,"aliases":[":usa:"]},{"name":"flag: Uruguay","shortname":":flag_uy:","category":9,"emoji_order":3817},{"name":"flag: Uzbekistan","shortname":":flag_uz:","category":9,"emoji_order":3818},{"name":"flag: Vatican City","shortname":":flag_va:","category":9,"emoji_order":3819},{"name":"flag: St. Vincent & Grenadines","shortname":":flag_vc:","category":9,"emoji_order":3820},{"name":"flag: Venezuela","shortname":":flag_ve:","category":9,"emoji_order":3821},{"name":"flag: British Virgin Islands","shortname":":flag_vg:","category":9,"emoji_order":3822},{"name":"flag: U.S. Virgin Islands","shortname":":flag_vi:","category":9,"emoji_order":3823},{"name":"flag: Vietnam","shortname":":flag_vn:","category":9,"emoji_order":3824},{"name":"flag: Vanuatu","shortname":":flag_vu:","category":9,"emoji_order":3825},{"name":"flag: Wallis & Futuna","shortname":":flag_wf:","category":9,"emoji_order":3826},{"name":"flag: Samoa","shortname":":flag_ws:","category":9,"emoji_order":3827},{"name":"flag: Kosovo","shortname":":flag_xk:","category":9,"emoji_order":3828},{"name":"flag: Yemen","shortname":":flag_ye:","category":9,"emoji_order":3829},{"name":"flag: Mayotte","shortname":":flag_yt:","category":9,"emoji_order":3830},{"name":"flag: South Africa","shortname":":flag_za:","category":9,"emoji_order":3831},{"name":"flag: Zambia","shortname":":flag_zm:","category":9,"emoji_order":3832},{"name":"flag: Zimbabwe","shortname":":flag_zw:","category":9,"emoji_order":3833},{"name":"flag: England","shortname":":flag_gbeng:","category":9,"emoji_order":3834,"aliases":[":england:"]},{"name":"flag: Scotland","shortname":":flag_gbsct:","category":9,"emoji_order":3835,"aliases":[":scotland:"]},{"name":"flag: Wales","shortname":":flag_gbwls:","category":9,"emoji_order":3836,"aliases":[":wales:"]}] \ No newline at end of file From 64e2de5b47ca2e081bbd38637e0e4132cfe853f5 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 20:56:07 +0100 Subject: [PATCH 116/273] make autocomplete work again --- src/components/views/rooms/MessageComposerInput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 2e933964d4..984ad2be27 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -534,7 +534,8 @@ export default class MessageComposerInput extends React.Component { // The first matched group includes just the matched plaintext emoji const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(text.slice(0, currentStartOffset)); if (emoticonMatch) { - const unicodeEmoji = EMOJIBASE.find(e => e.emoticon && e.emoticon.contains(emoticonMatch[1])); + const data = EMOJIBASE.find(e => e.emoticon === emoticonMatch[1]); + const unicodeEmoji = data ? data.unicode : ''; const range = Range.create({ anchor: { From 81338306b051c08cc3c19cc961fc41c016b6b32f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 21:00:14 +0100 Subject: [PATCH 117/273] fix lint --- src/autocomplete/EmojiProvider.js | 5 +---- src/components/views/messages/SenderProfile.js | 1 - src/components/views/messages/TextualEvent.js | 1 - src/components/views/rooms/MessageComposerInput.js | 4 +++- src/components/views/rooms/WhoIsTypingTile.js | 1 - 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 5846d319b2..51b1a3007c 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -20,7 +20,6 @@ import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import QueryMatcher from './QueryMatcher'; -import sdk from '../index'; import {PillCompletion} from './Components'; import type {Completion, SelectionRange} from './Autocompleter'; import _uniq from 'lodash/uniq'; @@ -28,9 +27,7 @@ import _sortBy from 'lodash/sortBy'; import SettingsStore from "../settings/SettingsStore"; import { shortcodeToUnicode } from '../HtmlUtils'; -import UNICODE_REGEX from 'emojibase-regex'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; -import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import EmojiData from '../stripped-emoji.json'; const LIMIT = 20; @@ -88,7 +85,7 @@ export default class EmojiProvider extends AutocompleteProvider { let completions = []; const {command, range} = this.getCurrentCommand(query, selection); if (command) { - let matchedString = command[0]; + const matchedString = command[0]; completions = this.matcher.match(matchedString); // Do second match with shouldMatchWordsOnly in order to match against 'name' diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index 2ccf5a3315..637a56727f 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -19,7 +19,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import {MatrixClient} from 'matrix-js-sdk'; -import sdk from '../../../index'; import Flair from '../elements/Flair.js'; import FlairStore from '../../../stores/FlairStore'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js index c94e79f2d9..a46e6cf8ec 100644 --- a/src/components/views/messages/TextualEvent.js +++ b/src/components/views/messages/TextualEvent.js @@ -20,7 +20,6 @@ const React = require('react'); import PropTypes from 'prop-types'; const TextForEvent = require('../../../TextForEvent'); -import sdk from '../../../index'; module.exports = React.createClass({ displayName: 'TextualEvent', diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 984ad2be27..df16310bda 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -1417,8 +1417,10 @@ export default class MessageComposerInput extends React.Component { ; } } - case 'emoji': + case 'emoji': { + const { data } = node; return data.get('emojiUnicode'); + } } }; diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 08fc6d2c70..c639b205d8 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import sdk from '../../../index'; import WhoIsTyping from '../../../WhoIsTyping'; import Timer from '../../../utils/Timer'; import MatrixClientPeg from '../../../MatrixClientPeg'; From b0ec594c5a3f2faed4c957d926dd6a65bc17e053 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 19 May 2019 21:19:20 +0100 Subject: [PATCH 118/273] comment on the futility of stripped-emoji.json --- src/autocomplete/EmojiProvider.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 51b1a3007c..9f0800c6ff 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -35,6 +35,9 @@ const LIMIT = 20; // Match for ascii-style ";-)" emoticons or ":wink:" shortcodes provided by emojibase const EMOJI_REGEX = new RegExp('(' + EMOTICON_REGEX.source + '|:[+-\\w]*:?)$', 'g'); +// XXX: it's very unclear why we bother with this generated emojidata file. +// all it means is that we end up bloating the bundle with precomputed stuff +// which would be trivial to calculate and cache on demand. const EMOJI_SHORTNAMES = Object.keys(EmojiData).map((key) => EmojiData[key]).sort( (a, b) => { if (a.category === b.category) { From 0c0052d06e0804d7e3e5e6ef017a3dfa73edf2d7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 20 May 2019 10:19:29 +0200 Subject: [PATCH 119/273] re-apply formatting when editor is closed --- src/components/views/messages/TextualBody.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index bd37f98360..380d04d1db 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -131,8 +131,9 @@ module.exports = React.createClass({ componentDidUpdate: function(prevProps) { if (!this.props.isEditing) { + const stoppedEditing = prevProps.isEditing && !this.props.isEditing; const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId; - if (messageWasEdited) { + if (messageWasEdited || stoppedEditing) { this._applyFormatting(); } this.calculateUrlPreview(); From 710338c01f9ac0d85045111266e46c15afef4526 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 19:48:05 +0100 Subject: [PATCH 120/273] pass member and room to editor pills to get avatar url --- src/components/views/elements/MessageEditor.js | 5 +++-- src/editor/autocomplete.js | 8 +++++--- src/editor/deserialize.js | 10 +++++----- src/editor/parts.js | 17 ++++++++++++++--- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index 0c249d067b..4f1e1675a5 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -40,16 +40,17 @@ export default class MessageEditor extends React.Component { constructor(props, context) { super(props, context); + const room = this.context.matrixClient.getRoom(this.props.event.getRoomId()); const partCreator = new PartCreator( () => this._autocompleteRef, query => this.setState({query}), + room, ); this.model = new EditorModel( - parseEvent(this.props.event), + parseEvent(this.props.event, room), partCreator, this._updateEditorState, ); - const room = this.context.matrixClient.getRoom(this.props.event.getRoomId()); this.state = { autoComplete: null, room, diff --git a/src/editor/autocomplete.js b/src/editor/autocomplete.js index d2f73b1dff..82d4086b45 100644 --- a/src/editor/autocomplete.js +++ b/src/editor/autocomplete.js @@ -17,11 +17,12 @@ limitations under the License. import {UserPillPart, RoomPillPart, PlainPart} from "./parts"; export default class AutocompleteWrapperModel { - constructor(updateCallback, getAutocompleterComponent, updateQuery) { + constructor(updateCallback, getAutocompleterComponent, updateQuery, room) { this._updateCallback = updateCallback; this._getAutocompleterComponent = getAutocompleterComponent; this._updateQuery = updateQuery; this._query = null; + this._room = room; } onEscape(e) { @@ -83,11 +84,12 @@ export default class AutocompleteWrapperModel { case "@": { const displayName = completion.completion; const userId = completion.completionId; - return new UserPillPart(userId, displayName); + const member = this._room.getMember(userId); + return new UserPillPart(userId, displayName, member); } case "#": { const displayAlias = completion.completionId; - return new RoomPillPart(displayAlias); + return new RoomPillPart(displayAlias, this._room); } // also used for emoji completion default: diff --git a/src/editor/deserialize.js b/src/editor/deserialize.js index a7f28badb1..0c9d090ea5 100644 --- a/src/editor/deserialize.js +++ b/src/editor/deserialize.js @@ -17,7 +17,7 @@ limitations under the License. import { MATRIXTO_URL_PATTERN } from '../linkify-matrix'; import { PlainPart, UserPillPart, RoomPillPart, NewlinePart } from "./parts"; -function parseHtmlMessage(html) { +function parseHtmlMessage(html, room) { const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); // no nodes from parsing here should be inserted in the document, // as scripts in event handlers, etc would be executed then. @@ -37,8 +37,8 @@ function parseHtmlMessage(html) { const resourceId = pillMatch[1]; // The room/user ID const prefix = pillMatch[2]; // The first character of prefix switch (prefix) { - case "@": return new UserPillPart(resourceId, n.textContent); - case "#": return new RoomPillPart(resourceId, n.textContent); + case "@": return new UserPillPart(resourceId, n.textContent, room.getMember(resourceId)); + case "#": return new RoomPillPart(resourceId, n.textContent, room); default: return new PlainPart(n.textContent); } } @@ -54,10 +54,10 @@ function parseHtmlMessage(html) { return parts; } -export function parseEvent(event) { +export function parseEvent(event, room) { const content = event.getContent(); if (content.format === "org.matrix.custom.html") { - return parseHtmlMessage(content.formatted_body || ""); + return parseHtmlMessage(content.formatted_body || "", room); } else { const body = content.body || ""; const lines = body.split("\n"); diff --git a/src/editor/parts.js b/src/editor/parts.js index bf792b1ab9..53d596ae2d 100644 --- a/src/editor/parts.js +++ b/src/editor/parts.js @@ -216,8 +216,9 @@ export class NewlinePart extends BasePart { } export class RoomPillPart extends PillPart { - constructor(displayAlias) { + constructor(displayAlias, room) { super(displayAlias, displayAlias); + this._room = room; } get type() { @@ -226,6 +227,11 @@ export class RoomPillPart extends PillPart { } export class UserPillPart extends PillPart { + constructor(userId, displayName, member) { + super(userId, displayName); + this._member = member; + } + get type() { return "user-pill"; } @@ -256,9 +262,14 @@ export class PillCandidatePart extends PlainPart { } export class PartCreator { - constructor(getAutocompleterComponent, updateQuery) { + constructor(getAutocompleterComponent, updateQuery, room) { this._autoCompleteCreator = (updateCallback) => { - return new AutocompleteWrapperModel(updateCallback, getAutocompleterComponent, updateQuery); + return new AutocompleteWrapperModel( + updateCallback, + getAutocompleterComponent, + updateQuery, + room, + ); }; } From 3b598a1782d6ad720622773cc6b59459b2c9de81 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 19:49:46 +0100 Subject: [PATCH 121/273] set pill avatar through css variables to set on psuedo-element this way it won't interfere with editor selection/caret --- res/css/views/elements/_MessageEditor.scss | 17 ++++++++++++++++- src/editor/parts.js | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index cc5649a224..49bd48b6bd 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -45,8 +45,23 @@ limitations under the License. display: inline-block; color: $primary-fg-color; background-color: $other-user-pill-bg-color; - padding-left: 5px; + padding-left: 21px; padding-right: 5px; + position: relative; + + &::before { + position: absolute; + left: 2px; + top: 2px; + content: var(--avatar-letter); + width: 16px; + height: 16px; + background: var(--avatar-background); //set on parent by JS + color: var(--avatar-color); + background-repeat: no-repeat; + background-size: 16px; + border-radius: 8px; + } } } diff --git a/src/editor/parts.js b/src/editor/parts.js index 53d596ae2d..f1f64f4b05 100644 --- a/src/editor/parts.js +++ b/src/editor/parts.js @@ -15,6 +15,7 @@ limitations under the License. */ import AutocompleteWrapperModel from "./autocomplete"; +import Avatar from "../Avatar"; class BasePart { constructor(text = "") { @@ -232,6 +233,20 @@ export class UserPillPart extends PillPart { this._member = member; } + toDOMNode() { + const pill = super.toDOMNode(); + const avatarUrl = Avatar.avatarUrlForMember(this._member, 16, 16); + if (avatarUrl) { + pill.style.setProperty("--avatar-background", `url('${avatarUrl}')`); + pill.style.setProperty("--avatar-letter", "''"); + } else { + pill.style.setProperty("--avatar-background", `green`); + pill.style.setProperty("--avatar-color", `white`); + pill.style.setProperty("--avatar-letter", `'${this.text[0].toUpperCase()}'`); + } + return pill; + } + get type() { return "user-pill"; } From a47e722fa1b3f909ffa07492fdec3e8235043f9b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 17 May 2019 19:51:03 +0100 Subject: [PATCH 122/273] same height as normal pill --- res/css/views/elements/_MessageEditor.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss index 49bd48b6bd..4cc44ab4eb 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/elements/_MessageEditor.scss @@ -41,6 +41,8 @@ limitations under the License. } span.user-pill, span.room-pill { + height: 20px; + line-height: 20px; border-radius: 16px; display: inline-block; color: $primary-fg-color; From f7968596e9e943af4a87bf4a35ea3517a74c33d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Sun, 19 May 2019 08:49:17 +0000 Subject: [PATCH 123/273] Translated using Weblate (French) Currently translated at 99.9% (1630 of 1632 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index f2652e3ba9..a30f98a4ce 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1970,5 +1970,6 @@ "Homeserver URL does not appear to be a valid Matrix homeserver": "L’URL du serveur d’accueil ne semble pas être un serveur d’accueil Matrix valide", "Invalid base_url for m.identity_server": "base_url pour m.identity_server non valide", "Identity server URL does not appear to be a valid identity server": "L’URL du serveur d’identité ne semble pas être un serveur d’identité valide", - "Edited at %(date)s": "Édité à %(date)s" + "Edited at %(date)s": "Édité à %(date)s", + "Show hidden events in timeline": "Afficher les évènements cachés dans l’historique" } From 8d583f49cc39550fac4f9ab5c62687fa11336c36 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sun, 19 May 2019 08:15:22 +0000 Subject: [PATCH 124/273] Translated using Weblate (Hungarian) Currently translated at 100.0% (1632 of 1632 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 8d52d4b12a..705a3625c9 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1970,5 +1970,6 @@ "reacted with %(shortName)s": "ezzel reagált: %(shortName)s", "Edited at %(date)s.": "Szerkesztve ekkor: %(date)s.", "edited": "szerkesztve", - "Edited at %(date)s": "Szerkesztve: %(date)s" + "Edited at %(date)s": "Szerkesztve: %(date)s", + "Show hidden events in timeline": "Rejtett események megmutatása az idővonalon" } From 7e5d51db6f591f8671c0363e5a4a4b27800bc68a Mon Sep 17 00:00:00 2001 From: Edgars Voroboks Date: Sun, 19 May 2019 04:58:02 +0000 Subject: [PATCH 125/273] Translated using Weblate (Latvian) Currently translated at 57.1% (932 of 1632 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lv/ --- src/i18n/strings/lv.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/lv.json b/src/i18n/strings/lv.json index 09182ed776..efd66b8f0e 100644 --- a/src/i18n/strings/lv.json +++ b/src/i18n/strings/lv.json @@ -1126,5 +1126,10 @@ "Collapse panel": "Sakļaut (saritināt) paneli", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Tavā pašreizējā pārlūkā aplikācijas izskats un uzvedība var būt pilnīgi neatbilstoša, kā arī dažas no visām funkcijām var nedarboties. Ja vēlies turpināt izmantot šo pārlūku, Tu vari arī turpināt, apzinoties, ka šajā gadījumā esi viens/a ar iespējamo problēmu!", "Checking for an update...": "Lūkojos pēc aktualizācijas...", - "There are advanced notifications which are not shown here": "Pastāv papildus paziņojumi, kuri šeit netiek rādīti" + "There are advanced notifications which are not shown here": "Pastāv papildus paziņojumi, kuri šeit netiek rādīti", + "e.g. %(exampleValue)s": "piemēram %(exampleValue)s", + "e.g. ": "piemēram ", + "Your device resolution": "Tavas iekārtas izšķirtspēja", + "Sign In": "Ienākt", + "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.": "Varat arī iestatīt pielāgotu identitātes serveri, bet jūs nevarēsiet uzaicināt lietotājus izmantojot e-pasta adresi, kā arī tikt uzaicināts pēc e-pasta adreses." } From e4d5f957858905bb78bf556b057912c80337dd32 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Sat, 18 May 2019 21:40:06 +0000 Subject: [PATCH 126/273] Translated using Weblate (Polish) Currently translated at 73.5% (1199 of 1632 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/pl/ --- src/i18n/strings/pl.json | 48 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json index 70e30c94bb..0e7eabb73f 100644 --- a/src/i18n/strings/pl.json +++ b/src/i18n/strings/pl.json @@ -128,7 +128,7 @@ "Are you sure you want to reject the invitation?": "Czy na pewno chcesz odrzucić zaproszenie?", "Are you sure you want to upload the following files?": "Czy na pewno chcesz przesłać następujące pliki?", "Autoplay GIFs and videos": "Automatycznie odtwarzaj GIFy i filmiki", - "%(senderName)s banned %(targetName)s.": "%(senderName)s zbanował %(targetName)s.", + "%(senderName)s banned %(targetName)s.": "%(senderName)s zbanował(a) %(targetName)s.", "Ban": "Zbanuj", "Bans user with given id": "Blokuje użytkownika o podanym ID", "Blacklisted": "Umieszczono na czarnej liście", @@ -456,7 +456,7 @@ "Unable to create widget.": "Nie można utworzyć widżetu.", "Unable to remove contact information": "Nie można usunąć informacji kontaktowych", "Unable to verify email address.": "Weryfikacja adresu e-mail nie powiodła się.", - "%(senderName)s unbanned %(targetName)s.": "%(senderName)s odblokował/a %(targetName)s.", + "%(senderName)s unbanned %(targetName)s.": "%(senderName)s odblokował(a) %(targetName)s.", "Unable to capture screen": "Nie można zrobić zrzutu ekranu", "Unable to enable Notifications": "Nie można włączyć powiadomień", "Unable to load device list": "Nie można załadować listy urządzeń", @@ -1393,5 +1393,47 @@ "Bell": "Dzwonek", "Anchor": "Kotwica", "Headphones": "Słuchawki", - "Folder": "Folder" + "Folder": "Folder", + "For maximum security, we recommend you do this in person or use another trusted means of communication.": "W celu zapewnienia maksymalnego bezpieczeństwa zalecamy, abyś zrobił to osobiście lub skorzystał z innego zaufanego środka komunikacji.", + "Phone Number": "Numer telefonu", + "Display Name": "Wyświetlana nazwa", + "Set a new account password...": "Ustaw nowe hasło do konta…", + "Email addresses": "Adresy E-mail", + "Phone numbers": "Numery telefonów", + "Language and region": "Język i region", + "Theme": "Motyw", + "Account management": "Zarządzanie kontem", + "Bug reporting": "Zgłaszanie błędów", + "Versions": "Wersje", + "Preferences": "Preferencje", + "Timeline": "Oś czasu", + "Room list": "Lista pokoi", + "Security & Privacy": "Bezpieczeństwo i prywatność", + "Room Addresses": "Adresy pokoju", + "Change room avatar": "Zmień awatar pokoju", + "Change room name": "Zmień nazwę pokoju", + "Change permissions": "Zmieniać uprawnienia", + "Change topic": "Zmieniać temat", + "Default role": "Domyślna rola", + "Send messages": "Wysyłanie wiadomości", + "Change settings": "Zmieniać ustawienia", + "Remove messages": "Usuwanie wiadomości", + "Notify everyone": "Powiadamianie wszystkich", + "Roles & Permissions": "Role i uprawnienia", + "Encryption": "Szyfrowanie", + "Join the conversation with an account": "Przyłącz się do rozmowy przy użyciu konta", + "Sign Up": "Zarejestruj się", + "Join the discussion": "Dołącz do dyskusji", + "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s nie może być wyświetlony. Chcesz do niego dołączyć?", + "Main address": "Główny adres", + "Room avatar": "Awatar pokoju", + "Upload room avatar": "Prześlij awatar pokoju", + "Room Name": "Nazwa pokoju", + "Room Topic": "Temat pokoju", + "Power level": "Poziom uprawnień", + "Verify by comparing a short text string.": "Weryfikuj, porównując krótki ciąg tekstu.", + "Begin Verifying": "Rozpocznij weryfikację", + "Waiting for partner to accept...": "Czekanie, aż partner zaakceptuje…", + "Room Settings - %(roomName)s": "Ustawienia pokoju - %(roomName)s", + "Doesn't look like a valid phone number": "To nie wygląda na poprawny numer telefonu" } From e3d3b155d60b99ba9868012870605089f2814c62 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 20 May 2019 10:08:34 +0100 Subject: [PATCH 127/273] remove errant debug --- src/autocomplete/AutocompleteProvider.js | 1 - src/autocomplete/EmojiProvider.js | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/autocomplete/AutocompleteProvider.js b/src/autocomplete/AutocompleteProvider.js index 3a561ffd0a..98ae83c526 100644 --- a/src/autocomplete/AutocompleteProvider.js +++ b/src/autocomplete/AutocompleteProvider.js @@ -61,7 +61,6 @@ export default class AutocompleteProvider { let match; while ((match = commandRegex.exec(query)) != null) { - console.log('Matched ' + JSON.stringify(match)); const start = match.index; const end = start + match[0].length; if (selection.start <= end && selection.end >= start) { diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 9f0800c6ff..8afcba6ab0 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -94,8 +94,6 @@ export default class EmojiProvider extends AutocompleteProvider { // Do second match with shouldMatchWordsOnly in order to match against 'name' completions = completions.concat(this.nameMatcher.match(matchedString)); - console.log("pre-sorted completions", completions); - const sorters = []; // make sure that emoticons come first sorters.push((c) => score(matchedString, c.aliases_ascii)); @@ -123,8 +121,6 @@ export default class EmojiProvider extends AutocompleteProvider { range, }; }).slice(0, LIMIT); - - console.log("mapped completions", completions); } return completions; } From 7b2602f1c75755f7f234fb0ecd766f4aeee9c0f1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 20 May 2019 10:09:19 +0100 Subject: [PATCH 128/273] apply monospace to pre --- res/css/_common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index b96836a2e1..d47bfdb7c0 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -32,7 +32,7 @@ body { margin: 0px; } -code { +pre, code { font-family: $monospace-font-family; } From e764ba4890f75793ae5a9f604668954d128cbbc1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 20 May 2019 10:10:30 +0100 Subject: [PATCH 129/273] remove accidental whitespace --- src/HtmlUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index d89cb3490b..f85ce00171 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -90,7 +90,7 @@ export function shortcodeToUnicode(shortcode) { return data ? data.unicode : null; } -export function processHtmlForSending (html: string): string { +export function processHtmlForSending(html: string): string { const contentDiv = document.createElement('div'); contentDiv.innerHTML = html; From e58d844e5bd237725daa9b929753341d613d80db Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 20 May 2019 14:20:36 +0200 Subject: [PATCH 130/273] move getInitialLetter to Avatar so we can reuse it for editor pills --- src/Avatar.js | 32 ++++++++++++++++++++++ src/components/views/avatars/BaseAvatar.js | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Avatar.js b/src/Avatar.js index 99b558fa93..67608da53d 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -58,4 +58,36 @@ module.exports = { } return require('../res/img/' + images[total % images.length] + '.png'); }, + + /** + * returns the first (non-sigil) character of 'name', + * converted to uppercase + */ + getInitialLetter(name) { + if (name.length < 1) { + return undefined; + } + + let idx = 0; + const initial = name[0]; + if ((initial === '@' || initial === '#' || initial === '+') && name[1]) { + idx++; + } + + // string.codePointAt(0) would do this, but that isn't supported by + // some browsers (notably PhantomJS). + let chars = 1; + const first = name.charCodeAt(idx); + + // check if it’s the start of a surrogate pair + if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) { + const second = name.charCodeAt(idx+1); + if (second >= 0xDC00 && second <= 0xDFFF) { + chars++; + } + } + + const firstChar = name.substring(idx, idx+chars); + return firstChar.toUpperCase(); + }, }; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 47de7c9dc4..10fb4f824a 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -176,7 +176,7 @@ module.exports = React.createClass({ } = this.props; if (imageUrl === this.state.defaultImageUrl) { - const initialLetter = this._getInitialLetter(name); + const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = (