From 80c2aa51b63d587ab5240b9a1fab5966a6e1d02f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Feb 2020 10:41:33 +0000 Subject: [PATCH 001/399] Transition BaseAvatar to hooks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintignore.errorfiles | 1 - src/components/views/avatars/BaseAvatar.js | 311 ++++++++++----------- 2 files changed, 144 insertions(+), 168 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 36b03b121c..e326f15002 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -7,7 +7,6 @@ src/components/structures/RoomView.js src/components/structures/ScrollPanel.js src/components/structures/SearchBox.js src/components/structures/UploadBar.js -src/components/views/avatars/BaseAvatar.js src/components/views/avatars/MemberAvatar.js src/components/views/create_room/RoomAlias.js src/components/views/dialogs/DeactivateAccountDialog.js diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 4c34cee853..1b5b28e1e3 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -2,7 +2,7 @@ Copyright 2015, 2016 OpenMarket Ltd Copyright 2018 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -17,206 +17,183 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; import PropTypes from 'prop-types'; -import createReactClass from 'create-react-class'; import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {useEventEmitter} from "../../../hooks/useEventEmitter"; -export default createReactClass({ - displayName: 'BaseAvatar', +const useImageUrl = ({url, urls, idName, name, defaultToInitialLetter}) => { + const [imageUrls, setUrls] = useState([]); + const [urlsIndex, setIndex] = useState(); - propTypes: { - name: PropTypes.string.isRequired, // The name (first initial used as default) - idName: PropTypes.string, // ID for generating hash colours - title: PropTypes.string, // onHover title text - url: PropTypes.string, // highest priority of them all, shortcut to set in urls[0] - urls: PropTypes.array, // [highest_priority, ... , lowest_priority] - width: PropTypes.number, - height: PropTypes.number, - // XXX resizeMethod not actually used. - resizeMethod: PropTypes.string, - defaultToInitialLetter: PropTypes.bool, // true to add default url - inputRef: PropTypes.oneOfType([ - // Either a function - PropTypes.func, - // Or the instance of a DOM native element - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - ]), - }, - - statics: { - contextType: MatrixClientContext, - }, - - getDefaultProps: function() { - return { - width: 40, - height: 40, - resizeMethod: 'crop', - defaultToInitialLetter: true, - }; - }, - - getInitialState: function() { - return this._getState(this.props); - }, - - componentDidMount() { - this.unmounted = false; - this.context.on('sync', this.onClientSync); - }, - - componentWillUnmount() { - this.unmounted = true; - this.context.removeListener('sync', this.onClientSync); - }, - - componentWillReceiveProps: function(nextProps) { - // work out if we need to call setState (if the image URLs array has changed) - const newState = this._getState(nextProps); - const newImageUrls = newState.imageUrls; - const oldImageUrls = this.state.imageUrls; - if (newImageUrls.length !== oldImageUrls.length) { - this.setState(newState); // detected a new entry - } else { - // check each one to see if they are the same - for (let i = 0; i < newImageUrls.length; i++) { - if (oldImageUrls[i] !== newImageUrls[i]) { - this.setState(newState); // detected a diff - break; - } - } + const onError = () => { + const nextIndex = urlsIndex + 1; + if (nextIndex < imageUrls.length) { + // try the next one + setIndex(nextIndex); } - }, + }; - onClientSync: function(syncState, prevState) { - if (this.unmounted) return; + const defaultImageUrl = useMemo(() => AvatarLogic.defaultAvatarUrlForString(idName || name), [idName, name]); - // Consider the client reconnected if there is no error with syncing. - // This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP. - const reconnected = syncState !== "ERROR" && prevState !== syncState; - if (reconnected && - // Did we fall back? - this.state.urlsIndex > 0 - ) { - // Start from the highest priority URL again - this.setState({ - urlsIndex: 0, - }); - } - }, - - _getState: function(props) { + useEffect(() => { // work out the full set of urls to try to load. This is formed like so: - // imageUrls: [ props.url, props.urls, default image ] + // imageUrls: [ props.url, ...props.urls, default image ] - let urls = []; + let _urls = []; if (!SettingsStore.getValue("lowBandwidth")) { - urls = props.urls || []; + _urls = urls || []; - if (props.url) { - urls.unshift(props.url); // put in urls[0] + if (url) { + _urls.unshift(url); // put in urls[0] } } - let defaultImageUrl = null; - if (props.defaultToInitialLetter) { - defaultImageUrl = AvatarLogic.defaultAvatarUrlForString( - props.idName || props.name, - ); - urls.push(defaultImageUrl); // lowest priority + if (defaultToInitialLetter) { + _urls.push(defaultImageUrl); // lowest priority } // deduplicate URLs - urls = Array.from(new Set(urls)); + _urls = Array.from(new Set(_urls)); - return { - imageUrls: urls, - defaultImageUrl: defaultImageUrl, - urlsIndex: 0, - }; - }, + setIndex(0); + setUrls(_urls); + }, [url, ...(urls || [])]); // eslint-disable-line react-hooks/exhaustive-deps - onError: function(ev) { - const nextIndex = this.state.urlsIndex + 1; - if (nextIndex < this.state.imageUrls.length) { - // try the next one - this.setState({ - urlsIndex: nextIndex, - }); + const cli = useContext(MatrixClientContext); + const onClientSync = useCallback((syncState, prevState) => { + // Consider the client reconnected if there is no error with syncing. + // This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP. + const reconnected = syncState !== "ERROR" && prevState !== syncState; + if (reconnected && urlsIndex > 0 ) { // Did we fall back? + // Start from the highest priority URL again + setIndex(0); } - }, + }, [urlsIndex]); + useEventEmitter(cli, "sync", onClientSync); - render: function() { - const imageUrl = this.state.imageUrls[this.state.urlsIndex]; + const imageUrl = imageUrls[urlsIndex]; + return [imageUrl, imageUrl === defaultImageUrl, onError]; +}; - const { - name, idName, title, url, urls, width, height, resizeMethod, - defaultToInitialLetter, onClick, inputRef, - ...otherProps - } = this.props; +const BaseAvatar = (props) => { + const { + name, + idName, + title, + url, + urls, + width=40, + height=40, + resizeMethod="crop", // eslint-disable-line no-unused-vars + defaultToInitialLetter=true, + onClick, + inputRef, + ...otherProps + } = props; - if (imageUrl === this.state.defaultImageUrl) { - const initialLetter = AvatarLogic.getInitialLetter(name); - const textNode = ( - - ); - const imgNode = ( - - ); - if (onClick != null) { - return ( - - { textNode } - { imgNode } - - ); - } else { - return ( - - { textNode } - { imgNode } - - ); - } - } + lineHeight: height + "px", + }} + > + { initialLetter } + + ); + const imgNode = ( + + ); + if (onClick != null) { return ( + > + { textNode } + { imgNode } + ); } else { return ( - + + { textNode } + { imgNode } + ); } - }, -}); + } + + if (onClick != null) { + return ( + + ); + } else { + return ( + + ); + } +}; + +BaseAvatar.displayName = "BaseAvatar"; + +BaseAvatar.propTypes = { + name: PropTypes.string.isRequired, // The name (first initial used as default) + idName: PropTypes.string, // ID for generating hash colours + title: PropTypes.string, // onHover title text + url: PropTypes.string, // highest priority of them all, shortcut to set in urls[0] + urls: PropTypes.array, // [highest_priority, ... , lowest_priority] + width: PropTypes.number, + height: PropTypes.number, + // XXX resizeMethod not actually used. + resizeMethod: PropTypes.string, + defaultToInitialLetter: PropTypes.bool, // true to add default url + onClick: PropTypes.func, + inputRef: PropTypes.oneOfType([ + // Either a function + PropTypes.func, + // Or the instance of a DOM native element + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), +}; + +export default BaseAvatar; From 384336e8f149612b22f77ae6a5967407864e736b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 Apr 2020 10:06:57 +0100 Subject: [PATCH 002/399] s/alias/address/ in copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/GroupAddressPicker.js | 2 +- src/SlashCommands.tsx | 8 ++-- src/components/structures/GroupView.js | 2 +- src/components/structures/RoomDirectory.js | 4 +- src/components/views/create_room/RoomAlias.js | 2 +- .../views/dialogs/CreateRoomDialog.js | 2 +- .../views/elements/RoomAliasField.js | 8 ++-- .../views/room_settings/AliasSettings.js | 14 +++---- .../tabs/room/SecurityRoomSettingsTab.js | 2 +- .../tabs/user/MjolnirUserSettingsTab.js | 4 +- src/i18n/strings/en_EN.json | 38 +++++++++---------- 11 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index 9131a89e5d..2928137f9d 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -73,7 +73,7 @@ export function showGroupAddRoomDialog(groupId) { title: _t("Add rooms to the community"), description: description, extraNode: checkboxContainer, - placeholder: _t("Room name or alias"), + placeholder: _t("Room name or address"), button: _t("Add to community"), pickerType: 'room', validAddressTypes: ['mx-room-id'], diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index aac42b6740..b205af477d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -441,8 +441,8 @@ export const Commands = [ new Command({ command: 'join', aliases: ['j', 'goto'], - args: '', - description: _td('Joins room with given alias'), + args: '', + description: _td('Joins room with given address'), runFn: function(_, args) { if (args) { // Note: we support 2 versions of this command. The first is @@ -553,7 +553,7 @@ export const Commands = [ }), new Command({ command: 'part', - args: '[]', + args: '[]', description: _td('Leave room'), runFn: function(roomId, args) { const cli = MatrixClientPeg.get(); @@ -585,7 +585,7 @@ export const Commands = [ } if (targetRoomId) break; } - if (!targetRoomId) return reject(_t('Unrecognised room alias:') + ' ' + roomAlias); + if (!targetRoomId) return reject(_t('Unrecognised room address:') + ' ' + roomAlias); } } diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 3b32e5c907..cae9e096a6 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -92,7 +92,7 @@ const CategoryRoomList = createReactClass({ Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, { title: _t('Add rooms to the community summary'), description: _t("Which rooms would you like to add to this summary?"), - placeholder: _t("Room name or alias"), + placeholder: _t("Room name or address"), button: _t("Add to summary"), pickerType: 'room', validAddressTypes: ['mx-room-id'], diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 0b07c10c8a..03cf06ec3f 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -199,7 +199,7 @@ export default createReactClass({ let desc; if (alias) { - desc = _t('Delete the room alias %(alias)s and remove %(name)s from the directory?', {alias: alias, name: name}); + desc = _t('Delete the room address %(alias)s and remove %(name)s from the directory?', {alias, name}); } else { desc = _t('Remove %(name)s from the directory?', {name: name}); } @@ -216,7 +216,7 @@ export default createReactClass({ MatrixClientPeg.get().setRoomDirectoryVisibility(room.room_id, 'private').then(() => { if (!alias) return; - step = _t('delete the alias.'); + step = _t('delete the address.'); return MatrixClientPeg.get().deleteAlias(alias); }).then(() => { modal.close(); diff --git a/src/components/views/create_room/RoomAlias.js b/src/components/views/create_room/RoomAlias.js index bc5dec1468..5bdfdde08d 100644 --- a/src/components/views/create_room/RoomAlias.js +++ b/src/components/views/create_room/RoomAlias.js @@ -98,7 +98,7 @@ export default createReactClass({ render: function() { return ( - ); diff --git a/src/components/views/dialogs/CreateRoomDialog.js b/src/components/views/dialogs/CreateRoomDialog.js index 88e90627e8..ec53f99b78 100644 --- a/src/components/views/dialogs/CreateRoomDialog.js +++ b/src/components/views/dialogs/CreateRoomDialog.js @@ -180,7 +180,7 @@ export default createReactClass({ let publicPrivateLabel; let aliasField; if (this.state.isPublic) { - publicPrivateLabel = (

{_t("Set a room alias to easily share your room with other people.")}

); + publicPrivateLabel = (

{_t("Set a room address to easily share your room with other people.")}

); const domain = MatrixClientPeg.get().getDomain(); aliasField = (
diff --git a/src/components/views/elements/RoomAliasField.js b/src/components/views/elements/RoomAliasField.js index d3de6a5d34..ee18913971 100644 --- a/src/components/views/elements/RoomAliasField.js +++ b/src/components/views/elements/RoomAliasField.js @@ -45,7 +45,7 @@ export default class RoomAliasField extends React.PureComponent { const maxlength = 255 - this.props.domain.length - 2; // 2 for # and : return ( allowEmpty || !!value, - invalid: () => _t("Please provide a room alias"), + invalid: () => _t("Please provide a room address"), }, { key: "taken", final: true, @@ -107,8 +107,8 @@ export default class RoomAliasField extends React.PureComponent { return !!err.errcode; } }, - valid: () => _t("This alias is available to use"), - invalid: () => _t("This alias is already in use"), + valid: () => _t("This address is available to use"), + invalid: () => _t("This address is already in use"), }, ], }); diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index 3994d78390..37d1e66e98 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -220,10 +220,10 @@ export default class AliasSettings extends React.Component { } }).catch((err) => { console.error(err); - Modal.createTrackedDialog('Error creating alias', '', ErrorDialog, { - title: _t("Error creating alias"), + Modal.createTrackedDialog('Error creating address', '', ErrorDialog, { + title: _t("Error creating address"), description: _t( - "There was an error creating that alias. It may not be allowed by the server " + + "There was an error creating that address. It may not be allowed by the server " + "or a temporary failure occurred.", ), }); @@ -245,15 +245,15 @@ export default class AliasSettings extends React.Component { console.error(err); let description; if (err.errcode === "M_FORBIDDEN") { - description = _t("You don't have permission to delete the alias."); + description = _t("You don't have permission to delete the address."); } else { description = _t( - "There was an error removing that alias. It may no longer exist or a temporary " + + "There was an error removing that address. It may no longer exist or a temporary " + "error occurred.", ); } - Modal.createTrackedDialog('Error removing alias', '', ErrorDialog, { - title: _t("Error removing alias"), + Modal.createTrackedDialog('Error removing address', '', ErrorDialog, { + title: _t("Error removing address"), description, }); }); diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js index eb2b885a22..c67596a3a5 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js @@ -247,7 +247,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
- {_t("To link to this room, please add an alias.")} + {_t("To link to this room, please add an address.")}
); diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js index d22b7ec183..f1fe5f2556 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js @@ -84,7 +84,7 @@ export default class MjolnirUserSettingsTab extends React.Component { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to subscribe to Mjolnir list', '', ErrorDialog, { title: _t('Error subscribing to list'), - description: _t('Please verify the room ID or alias and try again.'), + description: _t('Please verify the room ID or address and try again.'), }); } finally { this.setState({busy: false}); @@ -305,7 +305,7 @@ export default class MjolnirUserSettingsTab extends React.Component {
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ae8ed90402..3bbad1d585 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -114,7 +114,7 @@ "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?", "Add rooms to the community": "Add rooms to the community", - "Room name or alias": "Room name or alias", + "Room name or address": "Room name or address", "Add to community": "Add to community", "Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:", "Failed to invite users to community": "Failed to invite users to community", @@ -183,9 +183,9 @@ "Use an identity server": "Use an identity server", "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.", "Use an identity server to invite by email. Manage in Settings.": "Use an identity server to invite by email. Manage in Settings.", - "Joins room with given alias": "Joins room with given alias", + "Joins room with given address": "Joins room with given address", "Leave room": "Leave room", - "Unrecognised room alias:": "Unrecognised room alias:", + "Unrecognised room address:": "Unrecognised room address:", "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", @@ -788,7 +788,7 @@ "Error adding ignored user/server": "Error adding ignored user/server", "Something went wrong. Please try again or view your console for hints.": "Something went wrong. Please try again or view your console for hints.", "Error subscribing to list": "Error subscribing to list", - "Please verify the room ID or alias and try again.": "Please verify the room ID or alias and try again.", + "Please verify the room ID or address and try again.": "Please verify the room ID or address and try again.", "Error removing ignored user/server": "Error removing ignored user/server", "Error unsubscribing from list": "Error unsubscribing from list", "Please try again or view your console for hints.": "Please try again or view your console for hints.", @@ -815,7 +815,7 @@ "Subscribed lists": "Subscribed lists", "Subscribing to a ban list will cause you to join it!": "Subscribing to a ban list will cause you to join it!", "If this isn't what you want, please use a different tool to ignore users.": "If this isn't what you want, please use a different tool to ignore users.", - "Room ID or alias of ban list": "Room ID or alias of ban list", + "Room ID or address of ban list": "Room ID or address of ban list", "Subscribe": "Subscribe", "Notifications": "Notifications", "Start automatically after system login": "Start automatically after system login", @@ -916,7 +916,7 @@ "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.", "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "Click here to fix": "Click here to fix", - "To link to this room, please add an alias.": "To link to this room, please add an alias.", + "To link to this room, please add an address.": "To link to this room, please add an address.", "Only people who have been invited": "Only people who have been invited", "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests", @@ -1187,11 +1187,11 @@ "Error updating main address": "Error updating main address", "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.", "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.", - "Error creating alias": "Error creating alias", - "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.", - "You don't have permission to delete the alias.": "You don't have permission to delete the alias.", - "There was an error removing that alias. It may no longer exist or a temporary error occurred.": "There was an error removing that alias. It may no longer exist or a temporary error occurred.", - "Error removing alias": "Error removing alias", + "Error creating address": "Error creating address", + "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.": "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.", + "You don't have permission to delete the address.": "You don't have permission to delete the address.", + "There was an error removing that address. It may no longer exist or a temporary error occurred.": "There was an error removing that address. It may no longer exist or a temporary error occurred.", + "Error removing address": "Error removing address", "Main address": "Main address", "not specified": "not specified", "This room has no local addresses": "This room has no local addresses", @@ -1484,12 +1484,12 @@ "Custom level": "Custom level", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.", "In reply to ": "In reply to ", - "Room alias": "Room alias", + "Room address": "Room address", "e.g. my-room": "e.g. my-room", "Some characters not allowed": "Some characters not allowed", - "Please provide a room alias": "Please provide a room alias", - "This alias is available to use": "This alias is available to use", - "This alias is already in use": "This alias is already in use", + "Please provide a room address": "Please provide a room address", + "This address is available to use": "This address is available to use", + "This address is already in use": "This address is already in use", "Room directory": "Room directory", "Sign in with single sign-on": "Sign in with single sign-on", "And %(count)s more...|other": "And %(count)s more...", @@ -1556,7 +1556,7 @@ "example": "example", "Create": "Create", "Please enter a name for the room": "Please enter a name for the room", - "Set a room alias to easily share your room with other people.": "Set a room alias to easily share your room with other people.", + "Set a room address to easily share your room with other people.": "Set a room address to easily share your room with other people.", "This room is private, and can only be joined by invitation.": "This room is private, and can only be joined by invitation.", "Enable end-to-end encryption": "Enable end-to-end encryption", "You can’t disable this later. Bridges & most bots won’t work yet.": "You can’t disable this later. Bridges & most bots won’t work yet.", @@ -1807,7 +1807,7 @@ "Private Chat": "Private Chat", "Public Chat": "Public Chat", "Custom": "Custom", - "Alias (optional)": "Alias (optional)", + "Address (optional)": "Address (optional)", "Reject invitation": "Reject invitation", "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", "Unable to reject invite": "Unable to reject invite", @@ -2001,11 +2001,11 @@ "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.", "Riot failed to get the public room list.": "Riot failed to get the public room list.", "The homeserver may be unavailable or overloaded.": "The homeserver may be unavailable or overloaded.", - "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Delete the room alias %(alias)s and remove %(name)s from the directory?", + "Delete the room address %(alias)s and remove %(name)s from the directory?": "Delete the room address %(alias)s and remove %(name)s from the directory?", "Remove %(name)s from the directory?": "Remove %(name)s from the directory?", "Remove from Directory": "Remove from Directory", "remove %(name)s from the directory.": "remove %(name)s from the directory.", - "delete the alias.": "delete the alias.", + "delete the address.": "delete the address.", "The server may be unavailable or overloaded": "The server may be unavailable or overloaded", "Unable to join network": "Unable to join network", "Riot does not know how to join a room on this network": "Riot does not know how to join a room on this network", From f10d9e47e7c9e4d4ee20175256af3a2616940877 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 31 Mar 2020 16:02:23 +0100 Subject: [PATCH 003/399] Fix event tiles to smoothly resize with font. --- res/css/views/rooms/_EventTile.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 0dc60226b8..a6cff9cfb0 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -114,10 +114,9 @@ limitations under the License. clear: both; position: relative; padding-left: 65px; /* left gutter */ - padding-top: 4px; - padding-bottom: 2px; + padding-top: 3px; + padding-bottom: 3px; border-radius: 4px; - min-height: 24px; line-height: $font-22px; } From 1ff0f3445a52806888d82a2f25d95a120b9114cb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Fri, 3 Apr 2020 17:53:49 +0100 Subject: [PATCH 004/399] Fix pills. This was a hard pill to swallow --- res/css/views/elements/_RichText.scss | 35 +++++++++---------- res/css/views/rooms/_Autocomplete.scss | 9 ++--- .../views/rooms/_BasicMessageComposer.scss | 13 +++---- src/components/views/avatars/BaseAvatar.js | 22 ++++++++---- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index e01b1f8938..cb0c2eacc1 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -6,11 +6,14 @@ .mx_RoomPill, .mx_GroupPill, .mx_AtRoomPill { - border-radius: 16px; - display: inline-block; - height: 20px; + display: inline-flex; + align-items: center; + vertical-align: middle; + border-radius: $font-16px; + height: $font-16px; line-height: $font-20px; - padding-left: 5px; + padding-left: 0; + padding-right: 0.5em; } a.mx_Pill { @@ -19,6 +22,11 @@ a.mx_Pill { overflow: hidden; vertical-align: text-bottom; max-width: calc(100% - 1ch); + height: $font-24px; +} + +.mx_Pill { + padding: 0.3rem; } /* More specific to override `.markdown-body a` text-decoration */ @@ -31,8 +39,7 @@ a.mx_Pill { .mx_UserPill { color: $primary-fg-color; background-color: $other-user-pill-bg-color; - padding-right: 5px; -} + } .mx_UserPill_selected { background-color: $accent-color !important; @@ -45,7 +52,6 @@ a.mx_Pill { .mx_MessageComposer_input .mx_AtRoomPill { color: $accent-fg-color; background-color: $mention-user-pill-bg-color; - padding-right: 5px; } /* More specific to override `.markdown-body a` color */ @@ -55,15 +61,6 @@ a.mx_Pill { .mx_GroupPill { color: $accent-fg-color; background-color: $rte-room-pill-color; - padding-right: 5px; -} - -/* More specific to override `.markdown-body a` color */ -.mx_EventTile_content .markdown-body a.mx_GroupPill, -.mx_GroupPill { - color: $accent-fg-color; - background-color: $rte-group-pill-color; - padding-right: 5px; } .mx_EventTile_body .mx_UserPill, @@ -77,8 +74,10 @@ a.mx_Pill { .mx_GroupPill .mx_BaseAvatar, .mx_AtRoomPill .mx_BaseAvatar { position: relative; - left: -3px; - top: 2px; + display: inline-flex; + align-items: center; + border-radius: 10rem; + margin-right: 0.24rem; } .mx_Markdown_BOLD { diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index e5316f5a46..a4aebdb708 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -31,9 +31,10 @@ } .mx_Autocomplete_Completion_pill { - border-radius: 17px; - height: 34px; - padding: 0px 5px; + box-sizing: border-box; + border-radius: 2rem; + height: $font-34px; + padding: 0.4rem; display: flex; user-select: none; cursor: pointer; @@ -42,7 +43,7 @@ } .mx_Autocomplete_Completion_pill > * { - margin: 0 3px; + margin-right: 0.3rem; } /* styling for common completion elements */ diff --git a/res/css/views/rooms/_BasicMessageComposer.scss b/res/css/views/rooms/_BasicMessageComposer.scss index e9013eb7b7..e126e523a6 100644 --- a/res/css/views/rooms/_BasicMessageComposer.scss +++ b/res/css/views/rooms/_BasicMessageComposer.scss @@ -46,22 +46,19 @@ limitations under the License. &.mx_BasicMessageComposer_input_shouldShowPillAvatar { span.mx_UserPill, span.mx_RoomPill { - padding-left: 21px; position: relative; // avatar psuedo element &::before { - position: absolute; - left: 2px; - top: 2px; content: var(--avatar-letter); - width: 16px; - height: 16px; + width: $font-16px; + height: $font-16px; + margin-right: 0.24rem; background: var(--avatar-background), $avatar-bg-color; color: $avatar-initial-color; background-repeat: no-repeat; - background-size: 16px; - border-radius: 8px; + background-size: $font-16px; + border-radius: $font-16px; text-align: center; font-weight: normal; line-height: $font-16px; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 3e3a2e6bd9..d9336a81e7 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -164,9 +164,9 @@ export default createReactClass({ const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = ( @@ -174,7 +174,11 @@ export default createReactClass({ const imgNode = ( + aria-hidden="true" + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} /> ); if (onClick != null) { return ( @@ -202,7 +206,10 @@ export default createReactClass({ src={imageUrl} onClick={onClick} onError={this.onError} - width={width} height={height} + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} title={title} alt="" inputRef={inputRef} {...otherProps} /> @@ -213,7 +220,10 @@ export default createReactClass({ className="mx_BaseAvatar mx_BaseAvatar_image" src={imageUrl} onError={this.onError} - width={width} height={height} + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} title={title} alt="" ref={inputRef} {...otherProps} /> From 27b1ce841f4397a0bf6c362c7bc718efdd4571dc Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 16:23:16 +0100 Subject: [PATCH 005/399] Scale room tile heights. --- res/css/views/rooms/_RoomTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index de018bf178..5bc7d5624d 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: 34px; + height: $font-34px; margin: 0; padding: 0 8px 0 10px; position: relative; From f54ca219cf01a8c88f74abfb76b0f0cad7fae10c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 16:23:52 +0100 Subject: [PATCH 006/399] Scale read receipt images --- res/css/views/rooms/_EventTile.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index a6cff9cfb0..77bfa85862 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -310,6 +310,8 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody { .mx_EventTile_readAvatars .mx_BaseAvatar { position: absolute; display: inline-block; + height: $font-14px; + width: $font-14px; } .mx_EventTile_readAvatarRemainder { From d12ed333c1040d0bcd6fb4b00b7a40baeac433f4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 16:48:35 +0100 Subject: [PATCH 007/399] Fix avatar alignment for room state events --- 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 77bfa85862..1c781eec20 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -45,7 +45,7 @@ limitations under the License. } .mx_EventTile.mx_EventTile_info .mx_EventTile_avatar { - top: 8px; + top: $font-8px; left: 65px; } From 05d11fea6900b18c7d4063668188bfdf1c648772 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 16:49:44 +0100 Subject: [PATCH 008/399] Use a function to convert to rem. --- src/components/views/avatars/BaseAvatar.js | 19 ++++++++++--------- src/utils/rem.js | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/utils/rem.js diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index d9336a81e7..a17124b9ad 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,6 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import toRem from "../../../utils/rem"; export default createReactClass({ displayName: 'BaseAvatar', @@ -164,9 +165,9 @@ export default createReactClass({ const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = ( @@ -176,8 +177,8 @@ export default createReactClass({ alt="" title={title} onError={this.onError} aria-hidden="true" style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} /> ); if (onClick != null) { @@ -207,8 +208,8 @@ export default createReactClass({ onClick={onClick} onError={this.onError} style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} title={title} alt="" inputRef={inputRef} @@ -221,8 +222,8 @@ export default createReactClass({ src={imageUrl} onError={this.onError} style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} title={title} alt="" ref={inputRef} diff --git a/src/utils/rem.js b/src/utils/rem.js new file mode 100644 index 0000000000..0ba430950c --- /dev/null +++ b/src/utils/rem.js @@ -0,0 +1,20 @@ +/* +Copyright 2020 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. +*/ + +// converts a pixel value to rem. +export default function(pixel_val) { + return pixel_val / 15 + "rem" +} From c6bc0c79147b51c737709a202a90ebb3033e5eed Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 16:51:07 +0100 Subject: [PATCH 009/399] Fix read receipt horizontal spacing at scale --- src/components/views/rooms/ReadReceiptMarker.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index be0ca1f8d6..85d443d55a 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,6 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; +import toRem from "../../../utils/rem"; let bounce = false; try { @@ -148,7 +149,7 @@ export default createReactClass({ // start at the old height and in the old h pos startStyles.push({ top: startTopOffset+"px", - left: oldInfo.left+"px" }); + left: toRem(oldInfo.left) }); const reorderTransitionOpts = { duration: 100, @@ -181,7 +182,7 @@ export default createReactClass({ } const style = { - left: this.props.leftOffset+'px', + left: toRem(this.props.leftOffset), top: '0px', visibility: this.props.hidden ? 'hidden' : 'visible', }; From f29717ee62aceb982afd573fc0f473efad8c4488 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 17:47:12 +0100 Subject: [PATCH 010/399] Fix room header vertical alignment --- res/css/views/rooms/_RoomHeader.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 969106c9ea..80f6c40f39 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -173,8 +173,6 @@ limitations under the License. .mx_RoomHeader_avatar { flex: 0; - width: 28px; - height: 28px; margin: 0 7px; position: relative; } From 38919518da7bef490df539b08c2d72e4f23c5c22 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 6 Apr 2020 20:52:36 +0100 Subject: [PATCH 011/399] Resize room list scroll section. This fix isn't perfect. Currently the scroll view is slightly smaller than the list of rooms. I think it has something to do with the how the heigh is calculate in js, considering it has some assumptions about the height of each bar and the padding. However room items are the only things which change with respect to the root value. Therefore the item list is actually taller than the computed pixel value of the list converted to rems. I'll look into it. --- src/components/structures/RoomSubList.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 2ae2d71100..1e3e15b4ec 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,6 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; +import toRem from "../../utils/rem"; // turn this on for drop & drag console debugging galore const debug = false; @@ -383,7 +384,7 @@ export default class RoomSubList extends React.PureComponent { setHeight = (height) => { if (this._subList.current) { - this._subList.current.style.height = `${height}px`; + this._subList.current.style.height = toRem(height); } this._updateLazyRenderHeight(height); }; From cc601e1595e5bd4e571e74066e3b5cc1d3a8013f Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 14:21:21 +0100 Subject: [PATCH 012/399] Resize toggle switches with font --- res/css/views/elements/_ToggleSwitch.scss | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 1f4445b88c..9a07e01978 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -16,12 +16,19 @@ limitations under the License. .mx_ToggleSwitch { transition: background-color 0.20s ease-out 0.1s; - width: 48px; - height: 24px; - border-radius: 14px; + + width: $font-48px; + height: $font-20px; + border-radius: 1.5rem; + padding: 2px; + background-color: $togglesw-off-color; - position: relative; opacity: 0.5; + + display: flex; + flex-direction: row; + flex: 0 0 auto; + align-items: center; } .mx_ToggleSwitch_enabled { @@ -35,19 +42,12 @@ limitations under the License. .mx_ToggleSwitch_ball { transition: left 0.15s ease-out 0.1s; - margin: 2px; - width: 20px; - height: 20px; - border-radius: 20px; + width: $font-20px; + height: $font-20px; + border-radius: $font-20px; background-color: $togglesw-ball-color; - position: absolute; - top: 0; } -.mx_ToggleSwitch_on > .mx_ToggleSwitch_ball { - left: 23px; // 48px switch - 20px ball - 5px padding = 23px -} - -.mx_ToggleSwitch:not(.mx_ToggleSwitch_on) > .mx_ToggleSwitch_ball { - left: 2px; -} +.mx_ToggleSwitch_on { + flex-direction: row-reverse; +} \ No newline at end of file From 0182128189e433cd775f78a44f90ed0d523a961a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 15:07:34 +0100 Subject: [PATCH 013/399] Fix settings when scaling up --- res/css/views/elements/_ToggleSwitch.scss | 2 +- res/css/views/settings/tabs/_SettingsTab.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 9a07e01978..073bf5c00b 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_ToggleSwitch { transition: background-color 0.20s ease-out 0.1s; - width: $font-48px; + width: $font-44px; height: $font-20px; border-radius: 1.5rem; padding: 2px; diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 1fbfb35927..e3a61e6825 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -63,7 +63,7 @@ limitations under the License. display: inline-block; font-size: $font-14px; color: $primary-fg-color; - max-width: calc(100% - 48px); // Force word wrap instead of colliding with the switch + max-width: calc(100% - $font-48px); // Force word wrap instead of colliding with the switch box-sizing: border-box; padding-right: 10px; } From 5caec2a289a2f2b1bfc75cfeae1b05a9d3479b34 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 15:23:19 +0100 Subject: [PATCH 014/399] lint --- res/css/views/elements/_RichText.scss | 2 +- res/css/views/elements/_ToggleSwitch.scss | 4 ++-- src/utils/rem.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index cb0c2eacc1..21d01fdd51 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -39,7 +39,7 @@ a.mx_Pill { .mx_UserPill { color: $primary-fg-color; background-color: $other-user-pill-bg-color; - } +} .mx_UserPill_selected { background-color: $accent-color !important; diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 073bf5c00b..7c65fbc90d 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -21,7 +21,7 @@ limitations under the License. height: $font-20px; border-radius: 1.5rem; padding: 2px; - + background-color: $togglesw-off-color; opacity: 0.5; @@ -50,4 +50,4 @@ limitations under the License. .mx_ToggleSwitch_on { flex-direction: row-reverse; -} \ No newline at end of file +} diff --git a/src/utils/rem.js b/src/utils/rem.js index 0ba430950c..1f18c9de05 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -15,6 +15,6 @@ limitations under the License. */ // converts a pixel value to rem. -export default function(pixel_val) { - return pixel_val / 15 + "rem" +export default function(pixelVal) { + return pixelVal / 15 + "rem"; } From a58721047e6345ccd1b722c648314da7fbacac07 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 15:41:41 +0100 Subject: [PATCH 015/399] Room memebers avatars scale --- res/css/views/rooms/_EntityTile.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_EntityTile.scss b/res/css/views/rooms/_EntityTile.scss index 966d2c4e70..8db71f297c 100644 --- a/res/css/views/rooms/_EntityTile.scss +++ b/res/css/views/rooms/_EntityTile.scss @@ -69,8 +69,6 @@ limitations under the License. padding-right: 12px; padding-top: 4px; padding-bottom: 4px; - width: 36px; - height: 36px; position: relative; } From e49c041df79cb8169b39c76de21c087c5e21caec Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 16:08:59 +0100 Subject: [PATCH 016/399] Commuity seleciton bar vertical alignment scale fix --- res/css/structures/_TagPanel.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 0065ffa502..f5c4c4c698 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -110,13 +110,13 @@ limitations under the License. .mx_TagPanel .mx_TagTile.mx_TagTile_selected::before { content: ''; - height: 56px; + height: calc(100% + 16px); background-color: $accent-color; width: 5px; position: absolute; left: -15px; border-radius: 0 3px 3px 0; - top: -8px; // (56 - 40)/2 + top: -8px; // (16px / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { From 121b53f99a5f1d17decdd296ff4b960ffc026c3a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 16:27:26 +0100 Subject: [PATCH 017/399] FIx lanugage selection alignment at scale. --- res/css/views/elements/_Dropdown.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/elements/_Dropdown.scss b/res/css/views/elements/_Dropdown.scss index 32a68d5252..dd8511cc42 100644 --- a/res/css/views/elements/_Dropdown.scss +++ b/res/css/views/elements/_Dropdown.scss @@ -67,6 +67,8 @@ limitations under the License. text-overflow: ellipsis; white-space: nowrap; flex: 1; + display: inline-flex; + align-items: center; } .mx_Dropdown_option div { From 3966e45b8fccf884850e68469a9443795b0e830e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 17:13:09 +0100 Subject: [PATCH 018/399] Fix test --- test/components/views/messages/TextualBody-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 4ad46586ae..7801995254 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,8 @@ describe("", () => { 'Hey ' + '' + 'Member' + + 'style=\"width: 1.0666666666666667rem; height: 1.0666666666666667rem;\" ' + + 'title="@member:domain.bla" alt="" aria-hidden="true">Member' + ''); }); }); From 2d2b39892a2ad349514754f42725e86f33b58a54 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 17:23:07 +0100 Subject: [PATCH 019/399] lint test --- test/components/views/messages/TextualBody-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 7801995254..59671327ce 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,7 @@ describe("", () => { 'Hey ' + '' + 'Member' + ''); }); From c2a29087460969fd7368d84ff5112fb5b3b25d06 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 7 Apr 2020 17:41:18 +0100 Subject: [PATCH 020/399] Fix community line height --- res/css/structures/_TagPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index f5c4c4c698..4a78c8df92 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -69,7 +69,7 @@ limitations under the License. height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { - height: 40px; + height: $font-40px; padding: 10px 0 9px 0; } From c921859067b25b4c86d33ec93f3d06d6a0889ef0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:35:38 +0100 Subject: [PATCH 021/399] Use rem instead of em. --- res/css/views/elements/_RichText.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 21d01fdd51..d46d07e82b 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -13,7 +13,7 @@ height: $font-16px; line-height: $font-20px; padding-left: 0; - padding-right: 0.5em; + padding-right: 0.5rem; } a.mx_Pill { From 07853f48bb882a55ec2fdab9255bf2a8366d4efd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:45:21 +0100 Subject: [PATCH 022/399] Use font-variable instead of rem --- res/css/_font-sizes.scss | 7 +++++++ res/css/views/elements/_RichText.scss | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index ad9e2e7103..b37b2eb3c2 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -14,6 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +$font-1px: 0.067rem; +$font-2px: 0.133rem; +$font-3px: 0.200rem; +$font-4px: 0.267rem; +$font-5px: 0.333rem; +$font-6px: 0.400rem; +$font-7px: 0.467rem; $font-8px: 0.533rem; $font-9px: 0.600rem; $font-10px: 0.667rem; diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index d46d07e82b..5b5759b794 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -26,7 +26,7 @@ a.mx_Pill { } .mx_Pill { - padding: 0.3rem; + padding: $font-5px; } /* More specific to override `.markdown-body a` text-decoration */ From 18efbcccebece7ebd4cf17d5e6e9073261b3cb2a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 17:07:59 +0100 Subject: [PATCH 023/399] Undo superfluous delete --- res/css/views/elements/_RichText.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 5b5759b794..b933b1b828 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -29,6 +29,13 @@ a.mx_Pill { padding: $font-5px; } +/* More specific to override `.markdown-body a` color */ +.mx_EventTile_content .markdown-body a.mx_GroupPill, +.mx_GroupPill { + color: $accent-fg-color; + background-color: $rte-group-pill-color; +} + /* More specific to override `.markdown-body a` text-decoration */ .mx_EventTile_content .markdown-body a.mx_Pill { text-decoration: none; From fc04266b4239fe7f1135e81bf8d4a38b771d095d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 17:32:24 +0100 Subject: [PATCH 024/399] Skinnier pills are easier to swallow --- res/css/views/elements/_RichText.scss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index b933b1b828..1fa04595f9 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -10,10 +10,9 @@ align-items: center; vertical-align: middle; border-radius: $font-16px; - height: $font-16px; - line-height: $font-20px; + line-height: $font-15px; padding-left: 0; - padding-right: 0.5rem; + padding-right: $font-5px; } a.mx_Pill { @@ -22,11 +21,10 @@ a.mx_Pill { overflow: hidden; vertical-align: text-bottom; max-width: calc(100% - 1ch); - height: $font-24px; } .mx_Pill { - padding: $font-5px; + padding: $font-1px; } /* More specific to override `.markdown-body a` color */ From 7a0caafb77fadeabba717a78ae5ddd09e8fdaf33 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 19:15:34 +0100 Subject: [PATCH 025/399] FIx unread count --- res/css/structures/_RoomSubList.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 2e0c94263e..2c53258b08 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -74,7 +74,7 @@ limitations under the License. .mx_RoomSubList_badge > div { flex: 0 0 auto; - border-radius: 8px; + border-radius: $font-16px; font-weight: 600; font-size: $font-12px; padding: 0 5px; From 83609f0ab2bb1b75d0e0282eba5baee014e6f569 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 20:12:53 +0100 Subject: [PATCH 026/399] Scale user photo upload with font size --- res/css/_font-sizes.scss | 1 + res/css/views/settings/_AvatarSetting.scss | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index b37b2eb3c2..5cd19ce620 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -67,4 +67,5 @@ $font-49px: 3.267rem; $font-50px: 3.333rem; $font-51px: 3.400rem; $font-52px: 3.467rem; +$font-88px: 5.887rem; $font-400px: 26.667rem; diff --git a/res/css/views/settings/_AvatarSetting.scss b/res/css/views/settings/_AvatarSetting.scss index 35dba90f85..9fa10907b4 100644 --- a/res/css/views/settings/_AvatarSetting.scss +++ b/res/css/views/settings/_AvatarSetting.scss @@ -15,13 +15,13 @@ limitations under the License. */ .mx_AvatarSetting_avatar { - width: 88px; - height: 88px; + width: $font-88px; + height: $font-88px; margin-left: 13px; position: relative; & > * { - width: 88px; + width: $font-88px; box-sizing: border-box; } @@ -63,7 +63,7 @@ limitations under the License. & > img, .mx_AvatarSetting_avatarPlaceholder { display: block; - height: 88px; + height: $font-88px; border-radius: 4px; } From 42ec21f5bb306c51ae21e4e2b3995bc757139db8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 20:13:13 +0100 Subject: [PATCH 027/399] Tweak read receipt remainder text --- src/components/views/rooms/EventTile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index f67877373e..af14f6922c 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -34,6 +34,7 @@ import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; import * as ObjectUtils from "../../../ObjectUtils"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {E2E_STATE} from "./E2EIcon"; +import torem from "../../../utils/rem"; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -473,7 +474,7 @@ export default createReactClass({ if (remainder > 0) { remText = { remainder }+ + style={{ right: "calc(" + torem(-left) + " + " + receiptOffset + "px)" }}>{ remainder }+ ; } } From e10a511997ca6667bfb96b74938735c8c81447d5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 15:49:19 +0100 Subject: [PATCH 028/399] Extra right padding in user pills --- res/css/views/elements/_RichText.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 1fa04595f9..b45b567db7 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -25,6 +25,7 @@ a.mx_Pill { .mx_Pill { padding: $font-1px; + padding-right: $font-6px; } /* More specific to override `.markdown-body a` color */ From 61f2e19d95b8f7ef7589de0461d1877b9e607a11 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:18:57 +0100 Subject: [PATCH 029/399] Basic font settings. Include a default value getter in the store in order to make the default value easy to work with. --- src/settings/Settings.js | 7 ++++ src/settings/SettingsStore.js | 14 ++++++++ .../controllers/FontSizeController.js | 33 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/settings/controllers/FontSizeController.js diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 928b1985fa..3b316e39d0 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -29,6 +29,7 @@ import ThemeController from './controllers/ThemeController'; import PushToMatrixClientController from './controllers/PushToMatrixClientController'; import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases"; +import FontSizeController from './controllers/FontSizeController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -94,6 +95,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "font_size": { + displayName: _td("Font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 16, + controller: new FontSizeController(), + }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 0122916bc3..70ea5ac57c 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -370,6 +370,20 @@ export default class SettingsStore { return SettingsStore._getFinalValue(setting, level, roomId, null, null); } + /** + * Gets the default value of a setting. + * @param {string} settingName The name of the setting to read the value of. + * @return {*} The value, or null if not found + */ + static getDefaultValue(settingName, roomId = null, excludeDefault = false) { + // Verify that the setting is actually a setting + if (!SETTINGS[settingName]) { + throw new Error("Setting '" + settingName + "' does not appear to be a setting."); + } + + return SETTINGS[settingName].default; + } + static _getFinalValue(setting, level, roomId, calculatedValue, calculatedAtLevel) { let resultingValue = calculatedValue; diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js new file mode 100644 index 0000000000..4ab2f331f1 --- /dev/null +++ b/src/settings/controllers/FontSizeController.js @@ -0,0 +1,33 @@ +/* +Copyright 2020 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 SettingController from "./SettingController"; +import dis from "../../dispatcher"; + +export default class FontSizeController extends SettingController { + + constructor() { + super() + } + + onChange(level, roomId, newValue) { + // Dispatch font size change so that everything open responds to the change. + dis.dispatch({ + action: "update-font-size", + size: newValue, + }); + } +} From 269621ad2436f2181d44a10829cad1447249d8fd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:19:50 +0100 Subject: [PATCH 030/399] Move theme settings to a new tab --- .../tabs/user/GeneralUserSettingsTab.js | 172 ------------- .../tabs/user/StyleUserSettingsTab.js | 233 ++++++++++++++++++ src/theme.js | 2 +- 3 files changed, 234 insertions(+), 173 deletions(-) create mode 100644 src/components/views/settings/tabs/user/StyleUserSettingsTab.js diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 801062bff4..4e57203ca2 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -19,7 +19,6 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../../languageHandler"; import ProfileSettings from "../../ProfileSettings"; -import Field from "../../../elements/Field"; import * as languageHandler from "../../../../../languageHandler"; import {SettingLevel} from "../../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore"; @@ -27,7 +26,6 @@ import LanguageDropdown from "../../../elements/LanguageDropdown"; import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PropTypes from "prop-types"; -import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import PlatformPeg from "../../../../../PlatformPeg"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; @@ -60,9 +58,6 @@ export default class GeneralUserSettingsTab extends React.Component { }, emails: [], msisdns: [], - ...this._calculateThemeState(), - customThemeUrl: "", - customThemeMessage: {isError: false, text: ""}, }; this.dispatcherRef = dis.register(this._onAction); @@ -91,39 +86,6 @@ export default class GeneralUserSettingsTab extends React.Component { dis.unregister(this.dispatcherRef); } - _calculateThemeState() { - // We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we - // show the right values for things. - - const themeChoice = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"); - const systemThemeExplicit = SettingsStore.getValueAt( - SettingLevel.DEVICE, "use_system_theme", null, false, true); - const themeExplicit = SettingsStore.getValueAt( - SettingLevel.DEVICE, "theme", null, false, true); - - // If the user has enabled system theme matching, use that. - if (systemThemeExplicit) { - return { - theme: themeChoice, - useSystemTheme: true, - }; - } - - // If the user has set a theme explicitly, use that (no system theme matching) - if (themeExplicit) { - return { - theme: themeChoice, - useSystemTheme: false, - }; - } - - // Otherwise assume the defaults for the settings - return { - theme: themeChoice, - useSystemTheme: SettingsStore.getValueAt(SettingLevel.DEVICE, "use_system_theme"), - }; - } - _onAction = (payload) => { if (payload.action === 'id_server_changed') { this.setState({haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl())}); @@ -214,33 +176,6 @@ export default class GeneralUserSettingsTab extends React.Component { PlatformPeg.get().reload(); }; - _onThemeChange = (e) => { - const newTheme = e.target.value; - if (this.state.theme === newTheme) return; - - // doing getValue in the .catch will still return the value we failed to set, - // so remember what the value was before we tried to set it so we can revert - const oldTheme = SettingsStore.getValue('theme'); - SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => { - dis.dispatch({action: 'recheck_theme'}); - this.setState({theme: oldTheme}); - }); - this.setState({theme: newTheme}); - // The settings watcher doesn't fire until the echo comes back from the - // server, so to make the theme change immediately we need to manually - // do the dispatch now - // XXX: The local echoed value appears to be unreliable, in particular - // when settings custom themes(!) so adding forceTheme to override - // the value from settings. - dis.dispatch({action: 'recheck_theme', forceTheme: newTheme}); - }; - - _onUseSystemThemeChanged = (checked) => { - this.setState({useSystemTheme: checked}); - SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, checked); - dis.dispatch({action: 'recheck_theme'}); - }; - _onPasswordChangeError = (err) => { // TODO: Figure out a design that doesn't involve replacing the current dialog let errMsg = err.error || ""; @@ -277,41 +212,6 @@ export default class GeneralUserSettingsTab extends React.Component { }); }; - _onAddCustomTheme = async () => { - let currentThemes = SettingsStore.getValue("custom_themes"); - if (!currentThemes) currentThemes = []; - currentThemes = currentThemes.map(c => c); // cheap clone - - if (this._themeTimer) { - clearTimeout(this._themeTimer); - } - - try { - const r = await fetch(this.state.customThemeUrl); - const themeInfo = await r.json(); - if (!themeInfo || typeof(themeInfo['name']) !== 'string' || typeof(themeInfo['colors']) !== 'object') { - this.setState({customThemeMessage: {text: _t("Invalid theme schema."), isError: true}}); - return; - } - currentThemes.push(themeInfo); - } catch (e) { - console.error(e); - this.setState({customThemeMessage: {text: _t("Error downloading theme information."), isError: true}}); - return; // Don't continue on error - } - - await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes); - this.setState({customThemeUrl: "", customThemeMessage: {text: _t("Theme added!"), isError: false}}); - - this._themeTimer = setTimeout(() => { - this.setState({customThemeMessage: {text: "", isError: false}}); - }, 3000); - }; - - _onCustomThemeChange = (e) => { - this.setState({customThemeUrl: e.target.value}); - }; - _renderProfileSection() { return (
@@ -391,77 +291,6 @@ export default class GeneralUserSettingsTab extends React.Component { ); } - _renderThemeSection() { - const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); - const LabelledToggleSwitch = sdk.getComponent("views.elements.LabelledToggleSwitch"); - - const themeWatcher = new ThemeWatcher(); - let systemThemeSection; - if (themeWatcher.isSystemThemeSupported()) { - systemThemeSection =
- -
; - } - - let customThemeForm; - if (SettingsStore.isFeatureEnabled("feature_custom_themes")) { - let messageElement = null; - if (this.state.customThemeMessage.text) { - if (this.state.customThemeMessage.isError) { - messageElement =
{this.state.customThemeMessage.text}
; - } else { - messageElement =
{this.state.customThemeMessage.text}
; - } - } - customThemeForm = ( -
- - - {_t("Add theme")} - {messageElement} - -
- ); - } - - const themes = Object.entries(enumerateThemes()) - .map(p => ({id: p[0], name: p[1]})); // convert pairs to objects for code readability - const builtInThemes = themes.filter(p => !p.id.startsWith("custom-")); - const customThemes = themes.filter(p => !builtInThemes.includes(p)) - .sort((a, b) => a.name.localeCompare(b.name)); - const orderedThemes = [...builtInThemes, ...customThemes]; - return ( -
- {_t("Theme")} - {systemThemeSection} - - {orderedThemes.map(theme => { - return ; - })} - - {customThemeForm} - -
- ); - } - _renderDiscoverySection() { const SetIdServer = sdk.getComponent("views.settings.SetIdServer"); @@ -547,7 +376,6 @@ export default class GeneralUserSettingsTab extends React.Component { {this._renderProfileSection()} {this._renderAccountSection()} {this._renderLanguageSection()} - {this._renderThemeSection()}
{discoWarning} {_t("Discovery")}
{this._renderDiscoverySection()} {this._renderIntegrationManagerSection() /* Has its own title */} diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js new file mode 100644 index 0000000000..5c68f214d4 --- /dev/null +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -0,0 +1,233 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import {_t} from "../../../../../languageHandler"; +import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore"; +import * as sdk from "../../../../../index"; +import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; +import Field from "../../../elements/Field"; + +export default class StyleUserSettingsTab extends React.Component { + constructor() { + super(); + + this.state = { + fontSize: SettingsStore.getValue("font_size", null), + ...this._calculateThemeState(), + customThemeUrl: "", + customThemeMessage: {isError: false, text: ""}, + } + } + + _calculateThemeState() { + // We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we + // show the right values for things. + + const themeChoice = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"); + const systemThemeExplicit = SettingsStore.getValueAt( + SettingLevel.DEVICE, "use_system_theme", null, false, true); + const themeExplicit = SettingsStore.getValueAt( + SettingLevel.DEVICE, "theme", null, false, true); + + // If the user has enabled system theme matching, use that. + if (systemThemeExplicit) { + return { + theme: themeChoice, + useSystemTheme: true, + }; + } + + // If the user has set a theme explicitly, use that (no system theme matching) + if (themeExplicit) { + return { + theme: themeChoice, + useSystemTheme: false, + }; + } + + // Otherwise assume the defaults for the settings + return { + theme: themeChoice, + useSystemTheme: SettingsStore.getValueAt(SettingLevel.DEVICE, "use_system_theme"), + }; + } + + _onThemeChange = (e) => { + const newTheme = e.target.value; + if (this.state.theme === newTheme) return; + + // doing getValue in the .catch will still return the value we failed to set, + // so remember what the value was before we tried to set it so we can revert + const oldTheme = SettingsStore.getValue('theme'); + SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => { + dis.dispatch({action: 'recheck_theme'}); + this.setState({theme: oldTheme}); + }); + this.setState({theme: newTheme}); + // The settings watcher doesn't fire until the echo comes back from the + // server, so to make the theme change immediately we need to manually + // do the dispatch now + // XXX: The local echoed value appears to be unreliable, in particular + // when settings custom themes(!) so adding forceTheme to override + // the value from settings. + dis.dispatch({action: 'recheck_theme', forceTheme: newTheme}); + }; + + _onUseSystemThemeChanged = (checked) => { + this.setState({useSystemTheme: checked}); + SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, checked); + dis.dispatch({action: 'recheck_theme'}); + }; + + _onFontSizeChanged = (size) => { + let parsed_size = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + this.setState({fontSize: parsed_size}) + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsed_size) + }; + + _onAddCustomTheme = async () => { + let currentThemes = SettingsStore.getValue("custom_themes"); + if (!currentThemes) currentThemes = []; + currentThemes = currentThemes.map(c => c); // cheap clone + + if (this._themeTimer) { + clearTimeout(this._themeTimer); + } + + try { + const r = await fetch(this.state.customThemeUrl); + const themeInfo = await r.json(); + if (!themeInfo || typeof(themeInfo['name']) !== 'string' || typeof(themeInfo['colors']) !== 'object') { + this.setState({customThemeMessage: {text: _t("Invalid theme schema."), isError: true}}); + return; + } + currentThemes.push(themeInfo); + } catch (e) { + console.error(e); + this.setState({customThemeMessage: {text: _t("Error downloading theme information."), isError: true}}); + return; // Don't continue on error + } + + await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes); + this.setState({customThemeUrl: "", customThemeMessage: {text: _t("Theme added!"), isError: false}}); + + this._themeTimer = setTimeout(() => { + this.setState({customThemeMessage: {text: "", isError: false}}); + }, 3000); + }; + + _onCustomThemeChange = (e) => { + this.setState({customThemeUrl: e.target.value}); + }; + + render() { + return ( +
+
{_t("Style")}
+ {this._renderThemeSection()} + {this._renderFontSection()} +
+ ); + } + + _renderThemeSection() { + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); + const LabelledToggleSwitch = sdk.getComponent("views.elements.LabelledToggleSwitch"); + + const themeWatcher = new ThemeWatcher(); + let systemThemeSection; + if (themeWatcher.isSystemThemeSupported()) { + systemThemeSection =
+ +
; + } + + let customThemeForm; + if (SettingsStore.isFeatureEnabled("feature_custom_themes")) { + let messageElement = null; + if (this.state.customThemeMessage.text) { + if (this.state.customThemeMessage.isError) { + messageElement =
{this.state.customThemeMessage.text}
; + } else { + messageElement =
{this.state.customThemeMessage.text}
; + } + } + customThemeForm = ( +
+
+ + {_t("Add theme")} + {messageElement} + +
+ ); + } + + const themes = Object.entries(enumerateThemes()) + .map(p => ({id: p[0], name: p[1]})); // convert pairs to objects for code readability + const builtInThemes = themes.filter(p => !p.id.startsWith("custom-")); + const customThemes = themes.filter(p => !builtInThemes.includes(p)) + .sort((a, b) => a.name.localeCompare(b.name)); + const orderedThemes = [...builtInThemes, ...customThemes]; + return ( +
+ {_t("Theme")} + {systemThemeSection} + + {orderedThemes.map(theme => { + return ; + })} + + {customThemeForm} + +
+ ); + } + + _renderFontSection() { + return
+ {_t("Font size")} + this._onFontSizeChanged(ev.target.value)} + /> +
+ } + +} diff --git a/src/theme.js b/src/theme.js index 442a89e25f..3309acdd01 100644 --- a/src/theme.js +++ b/src/theme.js @@ -81,7 +81,7 @@ export class ThemeWatcher { } getEffectiveTheme() { - // Dev note: Much of this logic is replicated in the GeneralUserSettingsTab + // Dev note: Much of this logic is replicated in the StyleUserSettingsTab // XXX: checking the isLight flag here makes checking it in the ThemeController // itself completely redundant since we just override the result here and we're From bce1fa0c1a5277e87b46470055017f7f8a49d32e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:20:08 +0100 Subject: [PATCH 031/399] Simple translations --- src/i18n/strings/en_EN.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3eac055054..f10da54729 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2293,5 +2293,7 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "Font size": "Font size", + "Style": "Style" } From c1827925dad16eaac32c784dbbb13d7530dc7dd2 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:20:41 +0100 Subject: [PATCH 032/399] Use new style tab --- src/components/views/dialogs/UserSettingsDialog.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index b135d5f5f6..ee6f7e13ec 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,6 +22,7 @@ import {_t, _td} from "../../../languageHandler"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab"; import SettingsStore from "../../../settings/SettingsStore"; import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab"; +import StyleUserSettingsTab from "../settings/tabs/user/StyleUserSettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab"; import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab"; import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab"; @@ -66,6 +67,11 @@ export default class UserSettingsDialog extends React.Component { "mx_UserSettingsDialog_settingsIcon", , )); + tabs.push(new Tab( + _td("Style"), + "mx_userSettingsDialog_styleIcon", + , + )); tabs.push(new Tab( _td("Flair"), "mx_UserSettingsDialog_flairIcon", From af4dd2770c4cc3da7ef20617c2bb4fdb3e174a52 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 14 Apr 2020 16:21:04 +0100 Subject: [PATCH 033/399] Respond to font size changes --- src/components/structures/MatrixChat.js | 4 ++ src/fontSize.js | 50 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/fontSize.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1293ccc7e9..1845e0011d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -66,6 +66,7 @@ import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; +import { FontWatcher } from '../../fontSize'; /** constants for MatrixChat.state.view */ export const VIEWS = { @@ -265,7 +266,9 @@ export default createReactClass({ this.dispatcherRef = dis.register(this.onAction); this._themeWatcher = new ThemeWatcher(); + this._fontWatcher = new FontWatcher(10, 20); this._themeWatcher.start(); + this._fontWatcher.start(); this.focusComposer = false; @@ -353,6 +356,7 @@ export default createReactClass({ Lifecycle.stopMatrixClient(); dis.unregister(this.dispatcherRef); this._themeWatcher.stop(); + this._fontWatcher.stop(); window.removeEventListener("focus", this.onFocus); window.removeEventListener('resize', this.handleResize); this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); diff --git a/src/fontSize.js b/src/fontSize.js new file mode 100644 index 0000000000..c242fcc743 --- /dev/null +++ b/src/fontSize.js @@ -0,0 +1,50 @@ +/* +Copyright 2020 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 dis from './dispatcher'; +import SettingsStore from './settings/SettingsStore'; + +export class FontWatcher { + constructor(min_size, max_size) { + this._min_size = min_size; + this._max_size = max_size; + this._dispatcherRef = null; + } + + start() { + this._setRootFontSize(SettingsStore.getValue("font_size")); + this._dispatcherRef = dis.register(this._onAction); + } + + stop() { + dis.unregister(this._dispatcherRef); + } + + _onAction = (payload) => { + if (payload.action === 'update-font-size') { + this._setRootFontSize(payload.size); + } + }; + + _setRootFontSize = size => { + let fontSize = this._min_size < size?size:this._min_size; + fontSize = fontSize < this._max_size?fontSize:this._max_size; + if (fontSize != size) { + SettingsStore.setValue("font_size", null, fontSize) + } + document.querySelector(":root").style.fontSize = fontSize + "px"; + } +} \ No newline at end of file From 14551b1885bc0d309e284d0293b7ad0f042e9927 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 10:28:07 +0100 Subject: [PATCH 034/399] Hide font scaling behind labs --- .../views/settings/tabs/user/StyleUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index 5c68f214d4..ab9107eac0 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -139,7 +139,7 @@ export default class StyleUserSettingsTab extends React.Component {
{_t("Style")}
{this._renderThemeSection()} - {this._renderFontSection()} + {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null}
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f10da54729..bf2eaa4652 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2295,5 +2295,6 @@ "Space": "Space", "End": "End", "Font size": "Font size", + "Font scaling": "Font scaling", "Style": "Style" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 3b316e39d0..23b73f740b 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -95,6 +95,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "feature_font_scaling": { + isFeature: true, + displayName: _td("Font scaling"), + supportedLevels: LEVELS_FEATURE, + default: false + }, "font_size": { displayName: _td("Font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 0faf7b865f47a4fc28a0339d999f6592c501e045 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 10:31:40 +0100 Subject: [PATCH 035/399] Set font option width --- .../tabs/user/_GeneralUserSettingsTab.scss | 3 ++- .../tabs/user/_StyleUserSettingsTab.scss | 20 +++++++++++++++++++ .../tabs/user/StyleUserSettingsTab.js | 4 ++-- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 62d230e752..45aecd032f 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -15,7 +15,8 @@ limitations under the License. */ .mx_GeneralUserSettingsTab_changePassword .mx_Field, -.mx_GeneralUserSettingsTab_themeSection .mx_Field { +.mx_StyleUserSettingsTab_themeSection .mx_Field, +.mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss new file mode 100644 index 0000000000..dd9646bd5a --- /dev/null +++ b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss @@ -0,0 +1,20 @@ +/* +Copyright 2020 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_StyleUserSettingsTab_themeSection .mx_Field, +.mx_StyleUserSettingsTab_fontScaling .mx_Field { + @mixin mx_Settings_fullWidthField; +} \ No newline at end of file diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index ab9107eac0..e7b7385f5a 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -199,7 +199,7 @@ export default class StyleUserSettingsTab extends React.Component { .sort((a, b) => a.name.localeCompare(b.name)); const orderedThemes = [...builtInThemes, ...customThemes]; return ( -
+
{_t("Theme")} {systemThemeSection} + return
{_t("Font size")} Date: Thu, 16 Apr 2020 10:33:23 +0100 Subject: [PATCH 036/399] Make a font slider --- res/css/_components.scss | 1 + res/css/structures/_FontSlider.scss | 88 +++++++++++++++++++++++++ res/themes/light/css/_light.scss | 4 ++ src/components/structures/FontSlider.js | 62 +++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 res/css/structures/_FontSlider.scss create mode 100644 src/components/structures/FontSlider.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 0ba2b609e8..9d6629e703 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,6 +7,7 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; +@import "./structures/_FontSlider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss new file mode 100644 index 0000000000..94902f6fd9 --- /dev/null +++ b/res/css/structures/_FontSlider.scss @@ -0,0 +1,88 @@ +/* +Copyright 2020 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_fontSlider { + position: relative; + margin: 0px; + @mixin mx_Settings_fullWidthField; +} + +.mx_fontSlider_dotContainer { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.mx_fontSlider_bar { + display: flex; + box-sizing: border-box; + position: absolute; + height: 1rem; + width: 100%; + padding: 0 1.1rem; + align-items: center; +} + +.mx_fontSlider_bar > hr { + width: 100%; + border: 0.2rem solid $fontSlider-background-color; +} + +.mx_fontSlider_selection { + display: flex; + align-items: center; + width: calc(100% - 2.2rem); + height: 1rem; + position: absolute; +} + +.mx_fontSlider_selectionDot { + transition: left 0.25s; + position: absolute; + width: 1.1rem; + height: 1.1rem; + background-color: $fontSlider-selection-color; + border-radius: 50%; + box-shadow: 0 0 6px lightgrey; + z-index: 10; +} + +.mx_fontSlider_selection > hr { + transition: width 0.25s; + margin: 0; + border: 0.2rem solid $fontSlider-selection-color; +} + +.mx_fontSlider_dot { + transition: background-color 0.2s ease-in; + height: 1rem; + width: 1rem; + border-radius: 50%; + background-color: $fontSlider-background-color; + margin-bottom: 5px; + z-index: 0; +} + +.mx_fontSlider_dotActive { + background-color: $fontSlider-selection-color; +} + +.mx_fontSlider_dotValue { + display: flex; + flex-direction: column; + align-items: center; + color: $fontSlider-background-color; +} \ No newline at end of file diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index f5f3013354..b457a8bccb 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -262,6 +262,10 @@ $togglesw-off-color: #c1c9d6; $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; +// FontSlider +$fontSlider-selection-color: $accent-color; +$fontSlider-background-color: #c1c9d6; + $progressbar-color: #000; $room-warning-bg-color: $yellow-background; diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js new file mode 100644 index 0000000000..e2e36c2c9f --- /dev/null +++ b/src/components/structures/FontSlider.js @@ -0,0 +1,62 @@ +/* +Copyright 2020 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'; + +export default class Slider extends React.Component { + render() { + let dots = this.props.values.map(v => + this.props.updateFontSize(v)} + key={v} + />); + + let offset = this.offset(this.props.values, this.props.value); + + return
+
+
+
+
+
+
+
+
+
+ {dots} +
+
+
+ } + + offset(values, value) { + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + } +} + +class Dot extends React.Component { + render () { + let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + + return +
+
+ {this.props.value} +
+ + } +} \ No newline at end of file From 8b72756b8d987bb7218867dcf690ead46c0dea14 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 10:33:44 +0100 Subject: [PATCH 037/399] Use the font slider --- .../views/settings/tabs/user/StyleUserSettingsTab.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index e7b7385f5a..d77822ceb7 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -20,6 +20,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; +import FontSlider from "../../../../structures/FontSlider"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -219,11 +220,18 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return
{_t("Font size")} + value + 'px'} + /> this._onFontSizeChanged(ev.target.value)} /> From 647d99a17a6ff66816caee01f0258138a7a15493 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 10:33:59 +0100 Subject: [PATCH 038/399] Smooth font-size change transition --- res/css/_common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 03442ca510..687a238c8e 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,6 +19,7 @@ limitations under the License. @import "./_font-sizes.scss"; :root { + transition: font-size 0.25s; font-size: 15px; } From 66fd0f707fc290db64ba3a729c96ceedd402258d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 10:53:23 +0100 Subject: [PATCH 039/399] Type enforcement and comments --- src/components/structures/FontSlider.js | 35 ++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index e2e36c2c9f..0ae0092443 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -15,12 +15,29 @@ limitations under the License. */ import React from 'react'; +import PropTypes from 'prop-types'; export default class Slider extends React.Component { + + static propTypes = { + // A callback for the new value onclick + updateFontSize: PropTypes.func, + + // The current value of the slider + value: PropTypes.number, + + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: PropTypes.arrayOf(PropTypes.number), + + // A function for formatting the the values + displayFunc: PropTypes.func, + }; + render() { let dots = this.props.values.map(v => this.props.updateFontSize(v)} key={v} />); @@ -42,20 +59,32 @@ export default class Slider extends React.Component {
} - + offset(values, value) { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } } class Dot extends React.Component { + + static propTypes = { + // Callback for behaviour onclick + onClick: PropTypes.func, + + // Whether the dot should appear active + active: PropTypes.bool, + + // The label on the dot + label: PropTypes.string, + } + render () { let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return
- {this.props.value} + {this.props.label}
} From f1130ecba11be12bbc1e1efbf2823106501727e6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 11:56:43 +0100 Subject: [PATCH 040/399] Linting. Finally set up my linter properly --- src/components/structures/FontSlider.js | 28 +++++++++---------- .../tabs/user/StyleUserSettingsTab.js | 16 ++++++----- src/fontSize.js | 10 +++---- .../controllers/FontSizeController.js | 3 +- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index 0ae0092443..68173345ff 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -18,8 +18,8 @@ import React from 'react'; import PropTypes from 'prop-types'; export default class Slider extends React.Component { - static propTypes = { + // A callback for the new value onclick updateFontSize: PropTypes.func, @@ -32,41 +32,41 @@ export default class Slider extends React.Component { // A function for formatting the the values displayFunc: PropTypes.func, + }; render() { - let dots = this.props.values.map(v => - + this.props.updateFontSize(v)} key={v} />); - let offset = this.offset(this.props.values, this.props.value); + const offset = this.offset(this.props.values, this.props.value); return

-
-
+
+
{dots}
-
+
; } offset(values, value) { - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } } class Dot extends React.Component { - static propTypes = { // Callback for behaviour onclick onClick: PropTypes.func, @@ -78,14 +78,14 @@ class Dot extends React.Component { label: PropTypes.string, } - render () { - let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + render() { + const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return
{this.props.label}
- + ; } -} \ No newline at end of file +} diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index d77822ceb7..9cca0a2ce9 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -21,6 +21,8 @@ import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; import FontSlider from "../../../../structures/FontSlider"; +import AccessibleButton from "../../../elements/AccessibleButton"; +import dis from "../../../../../dispatcher"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -31,7 +33,8 @@ export default class StyleUserSettingsTab extends React.Component { ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, - } + + }; } _calculateThemeState() { @@ -95,9 +98,9 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - let parsed_size = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); - this.setState({fontSize: parsed_size}) - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsed_size) + const parsedSize = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + this.setState({fontSize: parsedSize}); + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); }; _onAddCustomTheme = async () => { @@ -221,7 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { return
{_t("Font size")} value + 'px'} @@ -235,7 +238,6 @@ export default class StyleUserSettingsTab extends React.Component { id="font_size_field" onChange={(ev) => this._onFontSizeChanged(ev.target.value)} /> -
+
; } - } diff --git a/src/fontSize.js b/src/fontSize.js index c242fcc743..8dbdb29102 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -18,9 +18,9 @@ import dis from './dispatcher'; import SettingsStore from './settings/SettingsStore'; export class FontWatcher { - constructor(min_size, max_size) { - this._min_size = min_size; - this._max_size = max_size; + constructor(minSize, maxSize) { + this._min_size = minSize; + this._max_size = maxSize; this._dispatcherRef = null; } @@ -43,8 +43,8 @@ export class FontWatcher { let fontSize = this._min_size < size?size:this._min_size; fontSize = fontSize < this._max_size?fontSize:this._max_size; if (fontSize != size) { - SettingsStore.setValue("font_size", null, fontSize) + SettingsStore.setValue("font_size", null, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; } -} \ No newline at end of file +} diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js index 4ab2f331f1..8e855e31ec 100644 --- a/src/settings/controllers/FontSizeController.js +++ b/src/settings/controllers/FontSizeController.js @@ -18,9 +18,8 @@ import SettingController from "./SettingController"; import dis from "../../dispatcher"; export default class FontSizeController extends SettingController { - constructor() { - super() + super(); } onChange(level, roomId, newValue) { From af3858fa98ccc5ff93e2e63b372b095626126390 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 12:09:36 +0100 Subject: [PATCH 041/399] Style linting --- res/css/structures/_FontSlider.scss | 3 ++- res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss index 94902f6fd9..fc83bd91bc 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_FontSlider.scss @@ -17,6 +17,7 @@ limitations under the License. .mx_fontSlider { position: relative; margin: 0px; + @mixin mx_Settings_fullWidthField; } @@ -85,4 +86,4 @@ limitations under the License. flex-direction: column; align-items: center; color: $fontSlider-background-color; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss index dd9646bd5a..f2a98ac426 100644 --- a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss @@ -17,4 +17,4 @@ limitations under the License. .mx_StyleUserSettingsTab_themeSection .mx_Field, .mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; -} \ No newline at end of file +} From 7c9df04d427d419c8df747eaef69e11e988e27a6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 20:26:04 +0100 Subject: [PATCH 042/399] Use "Appearance" instead of "Style" --- src/components/views/dialogs/UserSettingsDialog.js | 6 +++--- ...StyleUserSettingsTab.js => AppearanceUserSettingsTab.js} | 2 +- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/components/views/settings/tabs/user/{StyleUserSettingsTab.js => AppearanceUserSettingsTab.js} (99%) diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index ee6f7e13ec..91ab203753 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,7 +22,7 @@ import {_t, _td} from "../../../languageHandler"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab"; import SettingsStore from "../../../settings/SettingsStore"; import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab"; -import StyleUserSettingsTab from "../settings/tabs/user/StyleUserSettingsTab"; +import AppearanceUserSettingsTab from "../settings/tabs/user/AppearanceUserSettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab"; import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab"; import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab"; @@ -68,9 +68,9 @@ export default class UserSettingsDialog extends React.Component { , )); tabs.push(new Tab( - _td("Style"), + _td("Appearance"), "mx_userSettingsDialog_styleIcon", - , + , )); tabs.push(new Tab( _td("Flair"), diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js similarity index 99% rename from src/components/views/settings/tabs/user/StyleUserSettingsTab.js rename to src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 9cca0a2ce9..d09f4b3e6a 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -141,7 +141,7 @@ export default class StyleUserSettingsTab extends React.Component { render() { return (
-
{_t("Style")}
+
{_t("Appearance")}
{this._renderThemeSection()} {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bf2eaa4652..56798ff932 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2296,5 +2296,5 @@ "End": "End", "Font size": "Font size", "Font scaling": "Font scaling", - "Style": "Style" + "Appearance": "Appearance" } From b1452b5aa3bdd971aa2fa24c3bbd672ecd5d5255 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 20:47:29 +0100 Subject: [PATCH 043/399] Lint lint lint --- src/settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 23b73f740b..dd0103aa78 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -99,7 +99,7 @@ export const SETTINGS = { isFeature: true, displayName: _td("Font scaling"), supportedLevels: LEVELS_FEATURE, - default: false + default: false, }, "font_size": { displayName: _td("Font size"), From e455473d8fe1fba82c7c00603d746f8930b013e2 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 16 Apr 2020 20:54:11 +0100 Subject: [PATCH 044/399] i18n happy --- src/i18n/strings/en_EN.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 56798ff932..cff2b8cda4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -395,6 +395,8 @@ "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", + "Font scaling": "Font scaling", + "Font size": "Font size", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", @@ -742,22 +744,23 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Invalid theme schema.": "Invalid theme schema.", + "Error downloading theme information.": "Error downloading theme information.", + "Theme added!": "Theme added!", + "Appearance": "Appearance", + "Custom theme URL": "Custom theme URL", + "Add theme": "Add theme", + "Theme": "Theme", "Flair": "Flair", "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Success": "Success", "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them", - "Invalid theme schema.": "Invalid theme schema.", - "Error downloading theme information.": "Error downloading theme information.", - "Theme added!": "Theme added!", "Profile": "Profile", "Email addresses": "Email addresses", "Phone numbers": "Phone numbers", "Set a new account password...": "Set a new account password...", "Account": "Account", "Language and region": "Language and region", - "Custom theme URL": "Custom theme URL", - "Add theme": "Add theme", - "Theme": "Theme", "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", @@ -2293,8 +2296,5 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End", - "Font size": "Font size", - "Font scaling": "Font scaling", - "Appearance": "Appearance" + "End": "End" } From 787e408016e53a3293d1df336e0e2a5fc1ac50c5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 10:08:40 +0100 Subject: [PATCH 045/399] Explain origin of magic number Co-Authored-By: Travis Ralston --- res/css/structures/_TagPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 4a78c8df92..536c88be63 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -116,7 +116,7 @@ limitations under the License. position: absolute; left: -15px; border-radius: 0 3px 3px 0; - top: -8px; // (16px / 2) + top: -8px; // (16px from height / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { From 4d0cac1260af9dd9f4cdd930de47a202f0bee65b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 10:15:37 +0100 Subject: [PATCH 046/399] Render should be last method declared --- src/components/structures/FontSlider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index 68173345ff..aa4bfe42f5 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -35,6 +35,10 @@ export default class Slider extends React.Component { }; + _offset(values, value) { + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + } + render() { const dots = this.props.values.map(v => ); - const offset = this.offset(this.props.values, this.props.value); + const offset = this._offset(this.props.values, this.props.value); return
@@ -60,10 +64,6 @@ export default class Slider extends React.Component {
; } - - offset(values, value) { - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; - } } class Dot extends React.Component { From db1141b162e441ddf324c4e708f7c7c3c55297d9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 10:46:33 +0100 Subject: [PATCH 047/399] Move to typescript --- .../{FontSlider.js => FontSlider.tsx} | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) rename src/components/structures/{FontSlider.js => FontSlider.tsx} (73%) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.tsx similarity index 73% rename from src/components/structures/FontSlider.js rename to src/components/structures/FontSlider.tsx index aa4bfe42f5..9048e7b37b 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.tsx @@ -14,10 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; +import * as PropTypes from 'prop-types'; -export default class Slider extends React.Component { +type SliderProps = { + // A callback for the new value onclick + updateFontSize: (size: number) => null; + + // The current value of the slider + value: number; + + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: number[]; + + // A function for formatting the the values + displayFunc: (value: number) => string; + +} + +export default class Slider extends React.Component { static propTypes = { // A callback for the new value onclick @@ -35,11 +51,11 @@ export default class Slider extends React.Component { }; - _offset(values, value) { + _offset(values: number[], value: number): number { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } - render() { + render(): React.ReactNode { const dots = this.props.values.map(v => null, + + // Whether the dot should appear active + active: boolean, + + // The label on the dot + label: string, +} + +class Dot extends React.Component { static propTypes = { // Callback for behaviour onclick onClick: PropTypes.func, @@ -78,7 +105,7 @@ class Dot extends React.Component { label: PropTypes.string, } - render() { + render(): React.ReactNode { const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return From dd841fcde92f097a0cc056fa8f856d65c938620e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 11:01:52 +0100 Subject: [PATCH 048/399] Remove references to font --- res/css/structures/_FontSlider.scss | 20 ++++++++++---------- src/components/structures/FontSlider.tsx | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss index fc83bd91bc..2112ac9a88 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_FontSlider.scss @@ -14,20 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_fontSlider { +.mx_Slider { position: relative; margin: 0px; @mixin mx_Settings_fullWidthField; } -.mx_fontSlider_dotContainer { +.mx_Slider_dotContainer { display: flex; flex-direction: row; justify-content: space-between; } -.mx_fontSlider_bar { +.mx_Slider_bar { display: flex; box-sizing: border-box; position: absolute; @@ -37,12 +37,12 @@ limitations under the License. align-items: center; } -.mx_fontSlider_bar > hr { +.mx_Slider_bar > hr { width: 100%; border: 0.2rem solid $fontSlider-background-color; } -.mx_fontSlider_selection { +.mx_Slider_selection { display: flex; align-items: center; width: calc(100% - 2.2rem); @@ -50,7 +50,7 @@ limitations under the License. position: absolute; } -.mx_fontSlider_selectionDot { +.mx_Slider_selectionDot { transition: left 0.25s; position: absolute; width: 1.1rem; @@ -61,13 +61,13 @@ limitations under the License. z-index: 10; } -.mx_fontSlider_selection > hr { +.mx_Slider_selection > hr { transition: width 0.25s; margin: 0; border: 0.2rem solid $fontSlider-selection-color; } -.mx_fontSlider_dot { +.mx_Slider_dot { transition: background-color 0.2s ease-in; height: 1rem; width: 1rem; @@ -77,11 +77,11 @@ limitations under the License. z-index: 0; } -.mx_fontSlider_dotActive { +.mx_Slider_dotActive { background-color: $fontSlider-selection-color; } -.mx_fontSlider_dotValue { +.mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; diff --git a/src/components/structures/FontSlider.tsx b/src/components/structures/FontSlider.tsx index 9048e7b37b..7985fa206a 100644 --- a/src/components/structures/FontSlider.tsx +++ b/src/components/structures/FontSlider.tsx @@ -18,8 +18,8 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; type SliderProps = { - // A callback for the new value onclick - updateFontSize: (size: number) => null; + // A callback for the selected value + onSelectionChange: (value: number) => null; // The current value of the slider value: number; @@ -37,7 +37,7 @@ export default class Slider extends React.Component { static propTypes = { // A callback for the new value onclick - updateFontSize: PropTypes.func, + onSelectionChange: PropTypes.func, // The current value of the slider value: PropTypes.number, @@ -59,22 +59,22 @@ export default class Slider extends React.Component { const dots = this.props.values.map(v => this.props.updateFontSize(v)} + onClick={() => this.props.onSelectionChange(v)} key={v} />); const offset = this._offset(this.props.values, this.props.value); - return
+ return
-
+

-
-
+
+

-
+
{dots}
@@ -106,9 +106,9 @@ class Dot extends React.Component { } render(): React.ReactNode { - const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + const className = "mx_Slider_dot" + (this.props.active? " mx_Slider_dotActive": ""); - return + return
{this.props.label} From abd94a65bd4f5a8e60478fc52d3475318b63b562 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 11:29:37 +0100 Subject: [PATCH 049/399] Move compoenets/FontSlider to views/Slider --- res/css/_components.scss | 2 +- .../structures/{_FontSlider.scss => _Slider.scss} | 12 ++++++------ res/themes/light/css/_light.scss | 6 +++--- .../FontSlider.tsx => views/elements/Slider.tsx} | 2 ++ .../settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) rename res/css/structures/{_FontSlider.scss => _Slider.scss} (84%) rename src/components/{structures/FontSlider.tsx => views/elements/Slider.tsx} (96%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 9d6629e703..ab602be49e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,7 +7,7 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; -@import "./structures/_FontSlider.scss"; +@import "./structures/_Slider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_Slider.scss similarity index 84% rename from res/css/structures/_FontSlider.scss rename to res/css/structures/_Slider.scss index 2112ac9a88..51f1688f6b 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_Slider.scss @@ -39,7 +39,7 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2rem solid $fontSlider-background-color; + border: 0.2rem solid $Slider-background-color; } .mx_Slider_selection { @@ -55,7 +55,7 @@ limitations under the License. position: absolute; width: 1.1rem; height: 1.1rem; - background-color: $fontSlider-selection-color; + background-color: $Slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; z-index: 10; @@ -64,7 +64,7 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2rem solid $fontSlider-selection-color; + border: 0.2rem solid $Slider-selection-color; } .mx_Slider_dot { @@ -72,18 +72,18 @@ limitations under the License. height: 1rem; width: 1rem; border-radius: 50%; - background-color: $fontSlider-background-color; + background-color: $Slider-background-color; margin-bottom: 5px; z-index: 0; } .mx_Slider_dotActive { - background-color: $fontSlider-selection-color; + background-color: $Slider-selection-color; } .mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; - color: $fontSlider-background-color; + color: $Slider-background-color; } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index b457a8bccb..e06ba33594 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -262,9 +262,9 @@ $togglesw-off-color: #c1c9d6; $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; -// FontSlider -$fontSlider-selection-color: $accent-color; -$fontSlider-background-color: #c1c9d6; +// Slider +$Slider-selection-color: $accent-color; +$Slider-background-color: #c1c9d6; $progressbar-color: #000; diff --git a/src/components/structures/FontSlider.tsx b/src/components/views/elements/Slider.tsx similarity index 96% rename from src/components/structures/FontSlider.tsx rename to src/components/views/elements/Slider.tsx index 7985fa206a..2070f3f167 100644 --- a/src/components/structures/FontSlider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -16,6 +16,7 @@ limitations under the License. import * as React from 'react'; import * as PropTypes from 'prop-types'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; type SliderProps = { // A callback for the selected value @@ -93,6 +94,7 @@ type DotProps = { label: string, } +@replaceableComponent("views.elements.Dot") class Dot extends React.Component { static propTypes = { // Callback for behaviour onclick diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index d09f4b3e6a..9e9f134613 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -20,7 +20,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import FontSlider from "../../../../structures/FontSlider"; +import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -223,7 +223,7 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return
{_t("Font size")} - Date: Tue, 21 Apr 2020 11:34:15 +0100 Subject: [PATCH 050/399] Retain copyright Co-Authored-By: Travis Ralston --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 9e9f134613..7843ccbe10 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019, 2020 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. From 82974bd98c01fc673a8fb928f7890c7acd16641b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 11:37:22 +0100 Subject: [PATCH 051/399] Space out ternaries Co-Authored-By: Travis Ralston --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 7843ccbe10..8d1fd348d3 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -99,7 +99,7 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - const parsedSize = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + const parsedSize = isNaN(parseInt(size)) ? SettingsStore.getDefaultValue("font_size") : parseFloat(size); this.setState({fontSize: parsedSize}); SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); }; From 4525f71b1ce7925379fd9a67fab7b59f52d055b5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 11:40:18 +0100 Subject: [PATCH 052/399] Missed an import --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 8d1fd348d3..42c8e6d854 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -21,7 +21,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import Slider from "../../../elements/Slider"; +import FontSlider from "../../../../structures/FontSlider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -224,7 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return
{_t("Font size")} - Date: Tue, 21 Apr 2020 11:41:41 +0100 Subject: [PATCH 053/399] Move setting away from 'feature' settings for clarity --- src/settings/Settings.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index dd0103aa78..a044027baf 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -101,12 +101,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "font_size": { - displayName: _td("Font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 16, - controller: new FontSizeController(), - }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), @@ -177,6 +171,12 @@ export const SETTINGS = { displayName: _td("Show padlocks on invite only rooms"), default: true, }, + "font_size": { + displayName: _td("Font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 16, + controller: new FontSizeController(), + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 4397658bb32654b3a0427d4c1b761c43109e5825 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 11:56:12 +0100 Subject: [PATCH 054/399] Update file name in comments Co-Authored-By: Travis Ralston --- src/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.js b/src/theme.js index 3309acdd01..aee55b5abb 100644 --- a/src/theme.js +++ b/src/theme.js @@ -81,7 +81,7 @@ export class ThemeWatcher { } getEffectiveTheme() { - // Dev note: Much of this logic is replicated in the StyleUserSettingsTab + // Dev note: Much of this logic is replicated in the AppearanceUserSettingsTab // XXX: checking the isLight flag here makes checking it in the ThemeController // itself completely redundant since we just override the result here and we're From 315a272cb4aa1026f42723e7b1877f12e4ecffbc Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 12:03:32 +0100 Subject: [PATCH 055/399] File rename --- ...rSettingsTab.scss => _AppearanceUserSettingsTab.scss} | 4 ++-- .../settings/tabs/user/AppearanceUserSettingsTab.js | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) rename res/css/views/settings/tabs/user/{_StyleUserSettingsTab.scss => _AppearanceUserSettingsTab.scss} (85%) diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss similarity index 85% rename from res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss rename to res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index f2a98ac426..8c80a35e40 100644 --- a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_StyleUserSettingsTab_themeSection .mx_Field, -.mx_StyleUserSettingsTab_fontScaling .mx_Field { +.mx_AppearanceUserSettingsTab_themeSection .mx_Field, +.mx_AppearanceUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 42c8e6d854..738a5f9178 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -2,6 +2,7 @@ Copyright 2019 New Vector Ltd Copyright 2019, 2020 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 @@ -21,7 +22,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import FontSlider from "../../../../structures/FontSlider"; +import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -204,7 +205,7 @@ export default class StyleUserSettingsTab extends React.Component { .sort((a, b) => a.name.localeCompare(b.name)); const orderedThemes = [...builtInThemes, ...customThemes]; return ( -
+
{_t("Theme")} {systemThemeSection} + return
{_t("Font size")} - Date: Tue, 21 Apr 2020 12:06:10 +0100 Subject: [PATCH 056/399] Move slider themes --- res/css/_components.scss | 3 ++- res/css/{structures => views/elements}/_Slider.scss | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename res/css/{structures => views/elements}/_Slider.scss (100%) diff --git a/res/css/_components.scss b/res/css/_components.scss index ab602be49e..77a9b9f8cf 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,7 +7,6 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; -@import "./structures/_Slider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; @@ -114,6 +113,7 @@ @import "./views/elements/_RichText.scss"; @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_RoomAliasField.scss"; +@import "./views/elements/_Slider.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; @import "./views/elements/_TextWithTooltip.scss"; @@ -202,6 +202,7 @@ @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; +@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss"; @import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; @import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; @import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss"; diff --git a/res/css/structures/_Slider.scss b/res/css/views/elements/_Slider.scss similarity index 100% rename from res/css/structures/_Slider.scss rename to res/css/views/elements/_Slider.scss From dcea1f32b38c1856f05790a2eddcb014a498a486 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 16:18:25 +0100 Subject: [PATCH 057/399] tslint --- src/components/views/elements/Slider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 2070f3f167..f6ab121056 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -58,7 +58,7 @@ export default class Slider extends React.Component { render(): React.ReactNode { const dots = this.props.values.map(v => - this.props.onSelectionChange(v)} key={v} @@ -108,7 +108,7 @@ class Dot extends React.Component { } render(): React.ReactNode { - const className = "mx_Slider_dot" + (this.props.active? " mx_Slider_dotActive": ""); + const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); return
From 715bcb3c96e343b6c2a83d0ba21c16d2257e5be8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 21 Apr 2020 16:28:41 +0100 Subject: [PATCH 058/399] i18n match file moves --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cff2b8cda4..a3051cbb91 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -396,7 +396,6 @@ "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", "Font scaling": "Font scaling", - "Font size": "Font size", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", @@ -407,6 +406,7 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", + "Font size": "Font size", "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", From 0d0da6cfdc8d822cdcd8e6c14615aabc41724abf Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 10:19:17 +0100 Subject: [PATCH 059/399] Fix types, abandon propTypes --- src/components/views/elements/Slider.tsx | 45 ++++-------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index f6ab121056..13f06a4759 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -15,12 +15,10 @@ limitations under the License. */ import * as React from 'react'; -import * as PropTypes from 'prop-types'; -import { replaceableComponent } from '../../../utils/replaceableComponent'; -type SliderProps = { +type IProps = { // A callback for the selected value - onSelectionChange: (value: number) => null; + onSelectionChange: (value: number) => void; // The current value of the slider value: number; @@ -34,24 +32,7 @@ type SliderProps = { } -export default class Slider extends React.Component { - static propTypes = { - - // A callback for the new value onclick - onSelectionChange: PropTypes.func, - - // The current value of the slider - value: PropTypes.number, - - // The range and values of the slider - // Currently only supports an ascending, constant interval range - values: PropTypes.arrayOf(PropTypes.number), - - // A function for formatting the the values - displayFunc: PropTypes.func, - - }; - +export default class Slider extends React.Component { _offset(values: number[], value: number): number { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } @@ -83,9 +64,9 @@ export default class Slider extends React.Component { } } -type DotProps = { - // Callback for behaviour onclick - onClick: () => null, +type DotIProps = { + // Callback for behavior onclick + onClick: () => void, // Whether the dot should appear active active: boolean, @@ -94,19 +75,7 @@ type DotProps = { label: string, } -@replaceableComponent("views.elements.Dot") -class Dot extends React.Component { - static propTypes = { - // Callback for behaviour onclick - onClick: PropTypes.func, - - // Whether the dot should appear active - active: PropTypes.bool, - - // The label on the dot - label: PropTypes.string, - } - +class Dot extends React.Component { render(): React.ReactNode { const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); From ba362b727c5bdf7b6a76e8fdb3b5cdf1c3afdbc0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 10:19:37 +0100 Subject: [PATCH 060/399] Use onSelectionChange prop --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 738a5f9178..5bb6dcc0e0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -228,7 +228,7 @@ export default class StyleUserSettingsTab extends React.Component { value + 'px'} /> Date: Wed, 22 Apr 2020 10:24:29 +0100 Subject: [PATCH 061/399] Clamp indicated value within value range --- src/components/views/elements/Slider.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 13f06a4759..9f9e1fdef8 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -34,6 +34,17 @@ type IProps = { export default class Slider extends React.Component { _offset(values: number[], value: number): number { + const lowest = values[0]; + const highest = values[values.length - 1]; + + if (value < lowest) { + return 0; + } + + if (value > highest) { + return 100; + } + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } From 54a65441a54da04f6c4834fe089ac932778d0952 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 10:29:48 +0100 Subject: [PATCH 062/399] Lint ternary statement --- src/fontSize.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontSize.js b/src/fontSize.js index 8dbdb29102..d6b5b020f7 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -40,8 +40,8 @@ export class FontWatcher { }; _setRootFontSize = size => { - let fontSize = this._min_size < size?size:this._min_size; - fontSize = fontSize < this._max_size?fontSize:this._max_size; + let fontSize = this._min_size < size ? size : this._min_size; + fontSize = fontSize < this._max_size ? fontSize : this._max_size; if (fontSize != size) { SettingsStore.setValue("font_size", null, fontSize); } From 8d5965c33c5af0350da208ac8cfe8706dae049ce Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 10:32:00 +0100 Subject: [PATCH 063/399] Fix incorrect call to setValue --- src/fontSize.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontSize.js b/src/fontSize.js index d6b5b020f7..30c69b7428 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -15,7 +15,7 @@ limitations under the License. */ import dis from './dispatcher'; -import SettingsStore from './settings/SettingsStore'; +import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { constructor(minSize, maxSize) { @@ -43,7 +43,7 @@ export class FontWatcher { let fontSize = this._min_size < size ? size : this._min_size; fontSize = fontSize < this._max_size ? fontSize : this._max_size; if (fontSize != size) { - SettingsStore.setValue("font_size", null, fontSize); + SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; } From 26ccd6f07dcb598a04643e82412f6f801e8c65e9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 10:42:31 +0100 Subject: [PATCH 064/399] Cleaner clamping of value range --- src/components/views/elements/Slider.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 9f9e1fdef8..7862373c1c 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -34,18 +34,13 @@ type IProps = { export default class Slider extends React.Component { _offset(values: number[], value: number): number { - const lowest = values[0]; - const highest = values[values.length - 1]; + const min = values[0]; + const max = values[values.length - 1]; - if (value < lowest) { - return 0; - } + // Clamp value between min and max + value = Math.min(Math.max(value, min), max); - if (value > highest) { - return 100; - } - - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + return (value - min) / (max - min) * 100; } render(): React.ReactNode { From 5f50facfba28291514b258de1d26b4e691d5692a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 11:36:23 +0100 Subject: [PATCH 065/399] Make slider independant of label size --- res/css/views/elements/_Slider.scss | 16 ++++++++++++++-- src/components/views/elements/Slider.tsx | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 51f1688f6b..7e9acefa06 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -33,7 +33,7 @@ limitations under the License. position: absolute; height: 1rem; width: 100%; - padding: 0 1.1rem; + padding: 0 0.5rem; // half the width of a dot. align-items: center; } @@ -45,7 +45,7 @@ limitations under the License. .mx_Slider_selection { display: flex; align-items: center; - width: calc(100% - 2.2rem); + width: calc(100% - 1rem); // 2 * half the width of a dot height: 1rem; position: absolute; } @@ -87,3 +87,15 @@ limitations under the License. align-items: center; color: $Slider-background-color; } + +// The following is a hack to center the labels without adding +// any width to the slider's dots. +.mx_Slider_labelContainer { + width: 1rem; +} + +.mx_Slider_label { + position: relative; + width: fit-content; + left: -50%; +} diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 7862373c1c..e341eea317 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -87,9 +87,11 @@ class Dot extends React.Component { return
-
+
+
{this.props.label}
+
; } } From ee33fc1c20e93ee35120cd70d4c0caf156154cd0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 11:37:02 +0100 Subject: [PATCH 066/399] Remove labels --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 5bb6dcc0e0..046184da69 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -229,7 +229,7 @@ export default class StyleUserSettingsTab extends React.Component { values={[12, 14, 16, 18, 20]} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} - displayFunc={value => value + 'px'} + displayFunc={value => {}} /> Date: Wed, 22 Apr 2020 11:53:29 +0100 Subject: [PATCH 067/399] Add support to disable slider --- src/components/views/elements/Slider.tsx | 28 +++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index e341eea317..ad859bfe82 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -30,6 +30,8 @@ type IProps = { // A function for formatting the the values displayFunc: (value: number) => string; + // Whether the slider is disabled + disabled: boolean; } export default class Slider extends React.Component { @@ -47,8 +49,9 @@ export default class Slider extends React.Component { const dots = this.props.values.map(v => this.props.onSelectionChange(v)} + onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)} key={v} + disabled={this.props.disabled} />); const offset = this._offset(this.props.values, this.props.value); @@ -57,10 +60,13 @@ export default class Slider extends React.Component {

-
-
-
-
+ { this.props.disabled ? + null : +
+
+
+
+ }
{dots} @@ -79,18 +85,24 @@ type DotIProps = { // The label on the dot label: string, + + // Whether the slider is disabled + disabled: boolean; } class Dot extends React.Component { render(): React.ReactNode { - const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); + let className = "mx_Slider_dot" + if (!this.props.disabled && this.props.active) { + className += " mx_Slider_dotActive"; + } return
- {this.props.label} -
+ {this.props.label} +
; } From 1486beeaf42903fe75168a16ee3c45e7f516eb39 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 12:02:23 +0100 Subject: [PATCH 068/399] Make slider indpendent of settings styling --- res/css/views/elements/_Slider.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 7e9acefa06..2132381591 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -17,8 +17,6 @@ limitations under the License. .mx_Slider { position: relative; margin: 0px; - - @mixin mx_Settings_fullWidthField; } .mx_Slider_dotContainer { From 98799611cf31b5e2036a61ecac60d9b46d4f29b1 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 15:37:46 +0100 Subject: [PATCH 069/399] Remove padding for alignment reasons --- res/css/views/elements/_Slider.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 2132381591..83f100ff92 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -17,6 +17,7 @@ limitations under the License. .mx_Slider { position: relative; margin: 0px; + flex-grow: 1; } .mx_Slider_dotContainer { @@ -71,7 +72,6 @@ limitations under the License. width: 1rem; border-radius: 50%; background-color: $Slider-background-color; - margin-bottom: 5px; z-index: 0; } From f5d65907512971f4592f9be67d7c6ab2917ff0ef Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 16:11:01 +0100 Subject: [PATCH 070/399] Have max and min font configured in settings --- src/components/structures/MatrixChat.js | 2 +- src/fontSize.js | 11 ++++++----- src/settings/Settings.js | 10 ++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1845e0011d..602d85f048 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -266,7 +266,7 @@ export default createReactClass({ this.dispatcherRef = dis.register(this.onAction); this._themeWatcher = new ThemeWatcher(); - this._fontWatcher = new FontWatcher(10, 20); + this._fontWatcher = new FontWatcher(); this._themeWatcher.start(); this._fontWatcher.start(); diff --git a/src/fontSize.js b/src/fontSize.js index 30c69b7428..2e37921ee6 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -18,9 +18,7 @@ import dis from './dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - constructor(minSize, maxSize) { - this._min_size = minSize; - this._max_size = maxSize; + constructor() { this._dispatcherRef = null; } @@ -40,8 +38,11 @@ export class FontWatcher { }; _setRootFontSize = size => { - let fontSize = this._min_size < size ? size : this._min_size; - fontSize = fontSize < this._max_size ? fontSize : this._max_size; + const min = SettingsStore.getValue("font_size_min"); + const max = SettingsStore.getValue("font_size_max"); + + const fontSize = Math.max(Math.min(max, size), min); + if (fontSize != size) { SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index a044027baf..b144b07e84 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -177,6 +177,16 @@ export const SETTINGS = { default: 16, controller: new FontSizeController(), }, + "font_size_min": { + displayName: _td("Min font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 14, + }, + "font_size_max": { + displayName: _td("Max font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 24, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From fe175bb9a89d87307805582040e1f3295a2d0475 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 22 Apr 2020 17:31:49 +0100 Subject: [PATCH 071/399] Styling for the font slider --- .../tabs/user/_AppearanceUserSettingsTab.scss | 20 +++++++++++++++++++ .../tabs/user/AppearanceUserSettingsTab.js | 18 +++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 8c80a35e40..e4285e248c 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -14,7 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_AppearanceUserSettingsTab_fontSlider, .mx_AppearanceUserSettingsTab_themeSection .mx_Field, .mx_AppearanceUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } + +.mx_AppearanceUserSettingsTab_fontSlider { + display: flex; + flex-direction: row; + align-items: center; + padding: 10px; + background: #FFFFFF; + border-radius: 10px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_smallText { + font-size: 15px; + padding-right: 10px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_largeText { + font-size: 18px; + padding-left: 10px; +} diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 046184da69..e1bbaab2cc 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -25,6 +25,7 @@ import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; +import _range from "lodash/range"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -225,12 +226,17 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return
{_t("Font size")} - {}} - /> +
+
Aa
+ {}} + disabled={false} + /> +
Aa
+
Date: Thu, 23 Apr 2020 10:27:41 +0100 Subject: [PATCH 072/399] Linearly interpolate between value intervals. --- src/components/views/elements/Slider.tsx | 36 ++++++++++++++++--- .../tabs/user/AppearanceUserSettingsTab.js | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index ad859bfe82..a9fc41c8cc 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -35,14 +35,40 @@ type IProps = { } export default class Slider extends React.Component { + // offset is a terrible inverse approximation. + // if the values represents some function f(x) = y where x is the + // index of the array and y = values[x] then offset(f, y) = x + // s.t f(x) = y. + // it assumes a monotonic function and interpolates linearly between + // y values. + // Offset is used for finding the location of a value on a + // non linear slider. _offset(values: number[], value: number): number { - const min = values[0]; - const max = values[values.length - 1]; + // the index of the first number greater than value. + let closest = values.reduce((prev, curr) => { + return (value > curr ? prev + 1 : prev); + }, 0); - // Clamp value between min and max - value = Math.min(Math.max(value, min), max); + // Off the left + if (closest == 0) { + return 0; + } + + // Off the right + if (closest == values.length) { + return 100; + } + + // Now + const closestLessValue = values[closest - 1]; + const closestGreaterValue = values[closest]; + + const intervalWidth = 1 / (values.length - 1); + + const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue) + + return 100 * (closest - 1 + linearInterpolation) * intervalWidth - return (value - min) / (max - min) * 100; } render(): React.ReactNode { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index e1bbaab2cc..949b3bed31 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -229,7 +229,7 @@ export default class StyleUserSettingsTab extends React.Component {
Aa
{}} From a16fe09d4275b64939d34f56b1720cb00ef1e93e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 10:58:00 +0100 Subject: [PATCH 073/399] Use em to detach slider from root font-size --- res/css/views/elements/_Slider.scss | 22 +++++++++---------- .../tabs/user/_AppearanceUserSettingsTab.scss | 1 + src/components/views/elements/Slider.tsx | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 83f100ff92..f6982865db 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -30,30 +30,30 @@ limitations under the License. display: flex; box-sizing: border-box; position: absolute; - height: 1rem; + height: 1em; width: 100%; - padding: 0 0.5rem; // half the width of a dot. + padding: 0 0.5em; // half the width of a dot. align-items: center; } .mx_Slider_bar > hr { width: 100%; - border: 0.2rem solid $Slider-background-color; + border: 0.2em solid $Slider-background-color; } .mx_Slider_selection { display: flex; align-items: center; - width: calc(100% - 1rem); // 2 * half the width of a dot - height: 1rem; + width: calc(100% - 1em); // 2 * half the width of a dot + height: 1em; position: absolute; } .mx_Slider_selectionDot { transition: left 0.25s; position: absolute; - width: 1.1rem; - height: 1.1rem; + width: 1.1em; + height: 1.1em; background-color: $Slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; @@ -63,13 +63,13 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2rem solid $Slider-selection-color; + border: 0.2em solid $Slider-selection-color; } .mx_Slider_dot { transition: background-color 0.2s ease-in; - height: 1rem; - width: 1rem; + height: 1em; + width: 1em; border-radius: 50%; background-color: $Slider-background-color; z-index: 0; @@ -89,7 +89,7 @@ limitations under the License. // The following is a hack to center the labels without adding // any width to the slider's dots. .mx_Slider_labelContainer { - width: 1rem; + width: 1em; } .mx_Slider_label { diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index e4285e248c..28a2510508 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -27,6 +27,7 @@ limitations under the License. padding: 10px; background: #FFFFFF; border-radius: 10px; + font-size: 10px; } .mx_AppearanceUserSettingsTab_fontSlider_smallText { diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index a9fc41c8cc..6ec044da41 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -89,7 +89,7 @@ export default class Slider extends React.Component { { this.props.disabled ? null :
-
+

} From 6375e2526324de37ee170637c494bf08a29c4e59 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 11:22:51 +0100 Subject: [PATCH 074/399] Match padding from figma --- .../settings/tabs/user/_AppearanceUserSettingsTab.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 28a2510508..16a14edf85 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -24,7 +24,7 @@ limitations under the License. display: flex; flex-direction: row; align-items: center; - padding: 10px; + padding: 15px; background: #FFFFFF; border-radius: 10px; font-size: 10px; @@ -32,10 +32,12 @@ limitations under the License. .mx_AppearanceUserSettingsTab_fontSlider_smallText { font-size: 15px; - padding-right: 10px; + padding-right: 20px; + padding-left: 5px; } .mx_AppearanceUserSettingsTab_fontSlider_largeText { font-size: 18px; - padding-left: 10px; + padding-left: 20px; + padding-right: 5px; } From 28dca9e52529b3b8f49ee0e92cf448a4875fa403 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 11:33:28 +0100 Subject: [PATCH 075/399] Match figma color scheme --- .../views/settings/tabs/user/_AppearanceUserSettingsTab.scss | 2 +- res/themes/dark/css/_dark.scss | 3 +++ res/themes/light/css/_light.scss | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 16a14edf85..4141fb2fb1 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -25,7 +25,7 @@ limitations under the License. flex-direction: row; align-items: center; padding: 15px; - background: #FFFFFF; + background: $font-slider-bg-color; border-radius: 10px; font-size: 10px; } diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 5d6ba033c8..cb6e7ccdaa 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -177,6 +177,9 @@ $breadcrumb-placeholder-bg-color: #272c35; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: $room-highlight-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index e06ba33594..b576b57778 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -306,6 +306,9 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: $input-darker-bg-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { From a83993f1ff18f8c7be1d0949a8c983bbecf2ee9d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 11:49:54 +0100 Subject: [PATCH 076/399] Match margins in settings --- .../views/settings/tabs/user/_AppearanceUserSettingsTab.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 4141fb2fb1..e82ae3c575 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -28,6 +28,8 @@ limitations under the License. background: $font-slider-bg-color; border-radius: 10px; font-size: 10px; + margin-top: 24px; + margin-bottom: 24px; } .mx_AppearanceUserSettingsTab_fontSlider_smallText { From c86638c667d023abccb1250825e5c17e1070991c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 12:09:08 +0100 Subject: [PATCH 077/399] add toggle between font slider and custom setting --- .../settings/tabs/user/AppearanceUserSettingsTab.js | 11 +++++++++-- src/i18n/strings/en_EN.json | 3 +++ src/settings/Settings.js | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 949b3bed31..ceb3241b8b 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -36,7 +36,7 @@ export default class StyleUserSettingsTab extends React.Component { ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, - + useCustomFontSize: SettingsStore.getValue("useCustomFontSize"), }; } @@ -224,6 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { } _renderFontSection() { + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return
{_t("Font size")}
@@ -233,10 +234,15 @@ export default class StyleUserSettingsTab extends React.Component { value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} - disabled={false} + disabled={this.state.useCustomFontSize} />
Aa
+ this.setState({useCustomFontSize: checked})} + /> this._onFontSizeChanged(ev.target.value)} + disabled={!this.state.useCustomFontSize} />
; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3051cbb91..2c3239900d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -407,6 +407,9 @@ "Show info about bridges in room settings": "Show info about bridges in room settings", "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", "Font size": "Font size", + "Min font size": "Min font size", + "Max font size": "Max font size", + "Custom font size": "Custom font size", "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 b144b07e84..e0e34179f3 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -187,6 +187,11 @@ export const SETTINGS = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: 24, }, + "useCustomFontSize": { + displayName: _td("Custom font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: false, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 600a812227acafd8a15732cdabed3b6899415735 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 12:20:10 +0100 Subject: [PATCH 078/399] Add brush icon for appearance setting tab --- res/css/views/dialogs/_UserSettingsDialog.scss | 4 ++++ res/img/feather-customised/brush.svg | 5 +++++ src/components/views/dialogs/UserSettingsDialog.js | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 res/img/feather-customised/brush.svg diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 4d831d7858..7adcc58c4e 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -21,6 +21,10 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/settings.svg'); } +.mx_UserSettingsDialog_appearanceIcon::before { + mask-image: url('$(res)/img/feather-customised/brush.svg'); +} + .mx_UserSettingsDialog_voiceIcon::before { mask-image: url('$(res)/img/feather-customised/phone.svg'); } diff --git a/res/img/feather-customised/brush.svg b/res/img/feather-customised/brush.svg new file mode 100644 index 0000000000..d7f2738629 --- /dev/null +++ b/res/img/feather-customised/brush.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 91ab203753..bf06b8749f 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -69,7 +69,7 @@ export default class UserSettingsDialog extends React.Component { )); tabs.push(new Tab( _td("Appearance"), - "mx_userSettingsDialog_styleIcon", + "mx_UserSettingsDialog_appearanceIcon", , )); tabs.push(new Tab( From e5cb14929602cdc71887b3e95c54f546d9ccdda0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 13:52:08 +0100 Subject: [PATCH 079/399] Handle fontslider input errors correctly --- .../tabs/user/AppearanceUserSettingsTab.js | 33 ++++++++++++++++--- src/i18n/strings/en_EN.json | 3 ++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ceb3241b8b..4144605999 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -101,11 +101,33 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - const parsedSize = isNaN(parseInt(size)) ? SettingsStore.getDefaultValue("font_size") : parseFloat(size); - this.setState({fontSize: parsedSize}); - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); + this.setState({fontSize: size}); + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, size); }; + _onValidateFontSize = ({value}) => { + console.log({value}); + this.setState({fontSize: value}); + + const parsedSize = parseFloat(value); + const min = SettingsStore.getValue("font_size_min"); + const max = SettingsStore.getValue("font_size_max"); + + if (isNaN(parsedSize)) { + return {valid: false, feedback: _t("Size must be a number")}; + } + + console.log({min}); + console.log({max}); + console.log({parsedSize}); + if (!(min <= parsedSize && parsedSize <= max)) { + return {valid: false, feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max})}; + } + + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); + return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})}; + } + _onAddCustomTheme = async () => { let currentThemes = SettingsStore.getValue("custom_themes"); if (!currentThemes) currentThemes = []; @@ -247,10 +269,11 @@ export default class StyleUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={SettingsStore.getValue("font_size", null).toString()} + placeholder={this.state.fontSize} value={this.state.fontSize} id="font_size_field" - onChange={(ev) => this._onFontSizeChanged(ev.target.value)} + onValidate={this._onValidateFontSize} + onChange={({value}) => this.setState({fontSize: value})} disabled={!this.state.useCustomFontSize} />
; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2c3239900d..fa3d5e3f41 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -747,6 +747,9 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Size must be a number": "Size must be a number", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Custom font size can only be between %(min)s pt and %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Use between %(min)s pt and %(max)s pt", "Invalid theme schema.": "Invalid theme schema.", "Error downloading theme information.": "Error downloading theme information.", "Theme added!": "Theme added!", From a087f5ea400fd7a8fc7e6f207fdb15def6e4e2f3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 13:55:10 +0100 Subject: [PATCH 080/399] Lint --- .../tabs/user/AppearanceUserSettingsTab.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 4144605999..5c285d12e6 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -117,11 +117,11 @@ export default class StyleUserSettingsTab extends React.Component { return {valid: false, feedback: _t("Size must be a number")}; } - console.log({min}); - console.log({max}); - console.log({parsedSize}); if (!(min <= parsedSize && parsedSize <= max)) { - return {valid: false, feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max})}; + return { + valid: false, + feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max}), + }; } SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); @@ -252,7 +252,11 @@ export default class StyleUserSettingsTab extends React.Component {
Aa
{}} From 06f4eca05d51aaff8986406296ccb0844a66dfe4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 14:15:33 +0100 Subject: [PATCH 081/399] Background opacity --- res/themes/light/css/_light.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index b576b57778..ed7eae48f7 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -307,7 +307,7 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; // FontSlider colors -$font-slider-bg-color: $input-darker-bg-color; +$font-slider-bg-color: rgba($input-darker-bg-color, 0.2); // ***** Mixins! ***** From 4b4599c1d81b4200f41158cb8febe2fcb9121c3a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 23 Apr 2020 14:39:11 +0100 Subject: [PATCH 082/399] tslint --- src/components/views/elements/Slider.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 6ec044da41..559bdd9ce2 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -36,7 +36,7 @@ type IProps = { export default class Slider extends React.Component { // offset is a terrible inverse approximation. - // if the values represents some function f(x) = y where x is the + // if the values represents some function f(x) = y where x is the // index of the array and y = values[x] then offset(f, y) = x // s.t f(x) = y. // it assumes a monotonic function and interpolates linearly between @@ -50,16 +50,16 @@ export default class Slider extends React.Component { }, 0); // Off the left - if (closest == 0) { + if (closest === 0) { return 0; } // Off the right - if (closest == values.length) { + if (closest === values.length) { return 100; } - // Now + // Now const closestLessValue = values[closest - 1]; const closestGreaterValue = values[closest]; From 3962c98c9beaeee3f1685e63fe3604ed8a1a68eb Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Thu, 23 Apr 2020 22:53:02 +0300 Subject: [PATCH 083/399] Ensure PersistedElements are refreshed when AuxPanel scrolls If the screen is not tall enough, AuxPanel starts scrolling its content. If it contains PersistedElements, they need to be notified about scrolling as they only listen on resize events to move their element. Signed-off-by: Pauli Virtanen --- src/components/views/rooms/AuxPanel.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index e102b0dba4..00bdb1c45a 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -141,6 +141,15 @@ export default createReactClass({ return counters; }, + _onScroll: function(rect) { + if (this.props.onResize) { + this.props.onResize(); + } + + /* Force refresh of PersistedElements which may be partially hidden */ + window.dispatchEvent(new Event('resize')); + }, + render: function() { const CallView = sdk.getComponent("voip.CallView"); const TintableSvg = sdk.getComponent("elements.TintableSvg"); @@ -265,7 +274,7 @@ export default createReactClass({ } return ( - + { stateViews } { appsDrawer } { fileDropTarget } From d690d4bed2cb1562fface956ed0c4e34e4e35c54 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Tue, 21 Apr 2020 01:19:39 +0300 Subject: [PATCH 084/399] Prevent PersistedElements overflowing scrolled areas As the DOM element is not in reality contained inside "the parent", it may overflow the area if the parent gets partially hidden by scrolling etc. To make the effect visually less annoying, emulate this by clipping to the element wrapper. This is not a full general-purpose fix, but improves the current situation. Signed-off-by: Pauli Virtanen --- res/css/views/rooms/_AppsDrawer.scss | 4 ++ .../views/elements/PersistedElement.js | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 1b1bab67bc..e4743f189e 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -96,6 +96,10 @@ $AppsDrawerBodyHeight: 273px; height: $AppsDrawerBodyHeight; } +.mx_AppTile_persistedWrapper > div { + height: 100%; +} + .mx_AppTile_mini .mx_AppTile_persistedWrapper { height: 114px; } diff --git a/src/components/views/elements/PersistedElement.js b/src/components/views/elements/PersistedElement.js index 53f2501f19..18fa2aafef 100644 --- a/src/components/views/elements/PersistedElement.js +++ b/src/components/views/elements/PersistedElement.js @@ -156,16 +156,70 @@ export default class PersistedElement extends React.Component { child.style.display = visible ? 'block' : 'none'; } + /* + * Clip element bounding rectangle to that of the parent elements. + * This is not a full visibility check, but prevents the persisted + * element from overflowing parent containers when inside a scrolled + * area. + */ + _getClippedBoundingClientRect(element) { + let parentElement = element.parentElement; + let rect = element.getBoundingClientRect(); + + rect = new DOMRect(rect.left, rect.top, rect.width, rect.height); + + while (parentElement) { + const parentRect = parentElement.getBoundingClientRect(); + + if (parentRect.left > rect.left) { + rect.width = rect.width - (parentRect.left - rect.left); + rect.x = parentRect.x; + } + + if (parentRect.top > rect.top) { + rect.height = rect.height - (parentRect.top - rect.top); + rect.y = parentRect.y; + } + + if (parentRect.right < rect.right) { + rect.width = rect.width - (rect.right - parentRect.right); + } + + if (parentRect.bottom < rect.bottom) { + rect.height = rect.height - (rect.bottom - parentRect.bottom); + } + + parentElement = parentElement.parentElement; + } + + if (rect.width < 0) rect.width = 0; + if (rect.height < 0) rect.height = 0; + + return rect; + } + updateChildPosition(child, parent) { if (!child || !parent) return; const parentRect = parent.getBoundingClientRect(); + const clipRect = this._getClippedBoundingClientRect(parent); + + Object.assign(child.parentElement.style, { + position: 'absolute', + top: clipRect.top + 'px', + left: clipRect.left + 'px', + width: clipRect.width + 'px', + height: clipRect.height + 'px', + overflow: "hidden", + }); + Object.assign(child.style, { position: 'absolute', - top: parentRect.top + 'px', - left: parentRect.left + 'px', + top: (parentRect.top - clipRect.top) + 'px', + left: (parentRect.left - clipRect.left) + 'px', width: parentRect.width + 'px', height: parentRect.height + 'px', + overflow: "hidden", }); } From bfba5e6cfe8e2be24a135af072a6c0b2a41dbfbb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 27 Apr 2020 16:57:38 +0100 Subject: [PATCH 085/399] Fix member info avatar size --- src/components/views/avatars/BaseAvatar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index a17124b9ad..cbe083f3a2 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -208,8 +208,8 @@ export default createReactClass({ onClick={onClick} onError={this.onError} style={{ - width: toRem(width), - height: toRem(height) + width: {width}, + height: {height}, }} title={title} alt="" inputRef={inputRef} From a8407c9508a38b93465ab030e4d99ab8c86212ce Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:00:15 +0100 Subject: [PATCH 086/399] Use purecomponent Co-Authored-By: Travis Ralston --- src/components/views/elements/Slider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 559bdd9ce2..3dfd0c686e 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -116,7 +116,7 @@ type DotIProps = { disabled: boolean; } -class Dot extends React.Component { +class Dot extends React.PureComponent { render(): React.ReactNode { let className = "mx_Slider_dot" if (!this.props.disabled && this.props.active) { From c268b98ded295f9679ddf7eddef436a67ab86bb3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:17:50 +0100 Subject: [PATCH 087/399] Use faster lookup method Co-Authored-By: Travis Ralston --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 5c285d12e6..ed7d9ef495 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -168,7 +168,7 @@ export default class StyleUserSettingsTab extends React.Component {
{_t("Appearance")}
{this._renderThemeSection()} - {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null} + {SettingsStore.isFeatureEnabled("feature_font_scaling") ? this._renderFontSection() : null}
); } From f91613f112d2b33839f32eb5500fd3a95f796b95 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 13:53:16 +0100 Subject: [PATCH 088/399] Remove redundent selectors. Check _AppearanceUserSettingsTab --- res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 45aecd032f..5cc220bd33 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -15,8 +15,6 @@ limitations under the License. */ .mx_GeneralUserSettingsTab_changePassword .mx_Field, -.mx_StyleUserSettingsTab_themeSection .mx_Field, -.mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } From 137b94703aad9344b5f4ab38d4b6e7e441396ab9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 13:59:00 +0100 Subject: [PATCH 089/399] Lint types --- src/components/views/elements/Slider.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 3dfd0c686e..722401801c 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -16,22 +16,22 @@ limitations under the License. import * as React from 'react'; -type IProps = { - // A callback for the selected value - onSelectionChange: (value: number) => void; +interface IProps { + // A callback for the selected value + onSelectionChange: (value: number) => void; - // The current value of the slider - value: number; + // The current value of the slider + value: number; - // The range and values of the slider - // Currently only supports an ascending, constant interval range - values: number[]; + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: number[]; - // A function for formatting the the values - displayFunc: (value: number) => string; + // A function for formatting the the values + displayFunc: (value: number) => string; - // Whether the slider is disabled - disabled: boolean; + // Whether the slider is disabled + disabled: boolean; } export default class Slider extends React.Component { From 175b5e70b60f23dc446aba22da4f00d56aa2d624 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:09:54 +0100 Subject: [PATCH 090/399] Lint Slider --- src/components/views/elements/Slider.tsx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 722401801c..6712ddd7fd 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -43,7 +43,7 @@ export default class Slider extends React.Component { // y values. // Offset is used for finding the location of a value on a // non linear slider. - _offset(values: number[], value: number): number { + private offset(values: number[], value: number): number { // the index of the first number greater than value. let closest = values.reduce((prev, curr) => { return (value > curr ? prev + 1 : prev); @@ -80,19 +80,21 @@ export default class Slider extends React.Component { disabled={this.props.disabled} />); - const offset = this._offset(this.props.values, this.props.value); + let selection = null; + + if (this.props.disabled) { + const offset = this.offset(this.props.values, this.props.value); + selection =
+
+
+
+ } return

- { this.props.disabled ? - null : -
-
-
-
- } + { selection }
{dots} @@ -102,7 +104,7 @@ export default class Slider extends React.Component { } } -type DotIProps = { +interface IDotProps { // Callback for behavior onclick onClick: () => void, @@ -116,7 +118,7 @@ type DotIProps = { disabled: boolean; } -class Dot extends React.PureComponent { +class Dot extends React.PureComponent { render(): React.ReactNode { let className = "mx_Slider_dot" if (!this.props.disabled && this.props.active) { From 57d880ca5e2bde20a030f13d3f2257fe23e654b4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:24:44 +0100 Subject: [PATCH 091/399] Use correct name and indentation --- .../settings/tabs/user/AppearanceUserSettingsTab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ed7d9ef495..6c94a82c95 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -27,7 +27,7 @@ import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; import _range from "lodash/range"; -export default class StyleUserSettingsTab extends React.Component { +export default class AppearanceUserSettingsTab extends React.Component { constructor() { super(); @@ -231,9 +231,10 @@ export default class StyleUserSettingsTab extends React.Component {
{_t("Theme")} {systemThemeSection} - {orderedThemes.map(theme => { return ; From 93f24f12dcf839f35231e8ca9083f670c3f626d7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:26:08 +0100 Subject: [PATCH 092/399] Match filename to class --- src/{fontSize.js => FontWatcher.js} | 0 src/components/structures/MatrixChat.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{fontSize.js => FontWatcher.js} (100%) diff --git a/src/fontSize.js b/src/FontWatcher.js similarity index 100% rename from src/fontSize.js rename to src/FontWatcher.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 602d85f048..fec37472be 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -66,7 +66,7 @@ import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; -import { FontWatcher } from '../../fontSize'; +import { FontWatcher } from '../../FontWatcher'; /** constants for MatrixChat.state.view */ export const VIEWS = { From 9ca843fdcbc9f6d5d12dddac23a11af686cc702e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:27:18 +0100 Subject: [PATCH 093/399] Correct return type in docs Co-Authored-By: Travis Ralston --- src/settings/SettingsStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 70ea5ac57c..b6856a5a6a 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -373,7 +373,7 @@ export default class SettingsStore { /** * Gets the default value of a setting. * @param {string} settingName The name of the setting to read the value of. - * @return {*} The value, or null if not found + * @return {*} The default value */ static getDefaultValue(settingName, roomId = null, excludeDefault = false) { // Verify that the setting is actually a setting From fe326b9f08534d70c36b0484e727304e8396ba8c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:30:56 +0100 Subject: [PATCH 094/399] Enfore function name capitalisation --- src/components/structures/RoomSubList.js | 2 +- src/components/views/avatars/BaseAvatar.js | 2 +- src/components/views/rooms/EventTile.js | 4 ++-- src/components/views/rooms/ReadReceiptMarker.js | 2 +- src/utils/rem.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 1e3e15b4ec..b1e0bb9f9b 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,7 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; -import toRem from "../../utils/rem"; +import {toRem} from "../../utils/rem"; // turn this on for drop & drag console debugging galore const debug = false; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index cbe083f3a2..e94a83f70b 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,7 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import toRem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; export default createReactClass({ displayName: 'BaseAvatar', diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index af14f6922c..0881fb3b67 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -34,7 +34,7 @@ import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; import * as ObjectUtils from "../../../ObjectUtils"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {E2E_STATE} from "./E2EIcon"; -import torem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -474,7 +474,7 @@ export default createReactClass({ if (remainder > 0) { remText = { remainder }+ + style={{ right: "calc(" + toRem(-left) + " + " + receiptOffset + "px)" }}>{ remainder }+ ; } } diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 85d443d55a..20d39a7f84 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; -import toRem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; let bounce = false; try { diff --git a/src/utils/rem.js b/src/utils/rem.js index 1f18c9de05..6278a91aa2 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -15,6 +15,6 @@ limitations under the License. */ // converts a pixel value to rem. -export default function(pixelVal) { +export function toRem(pixelVal) { return pixelVal / 15 + "rem"; } From 1289367a6b63f8e04e53da6ae5a2ae7e5a8e5455 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 14:31:24 +0100 Subject: [PATCH 095/399] Fix indentation --- src/utils/rem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rem.js b/src/utils/rem.js index 6278a91aa2..3729b4d596 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -16,5 +16,5 @@ limitations under the License. // converts a pixel value to rem. export function toRem(pixelVal) { - return pixelVal / 15 + "rem"; + return pixelVal / 15 + "rem"; } From eb72245493c1dbe163f85afbf97a26b68078e525 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 15:19:12 +0100 Subject: [PATCH 096/399] fix syntax error --- res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 5cc220bd33..0af7e30d97 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GeneralUserSettingsTab_changePassword .mx_Field, +.mx_GeneralUserSettingsTab_changePassword .mx_Field { @mixin mx_Settings_fullWidthField; } From af8430b98aa5e47116e76ce3547da955ad18b1dd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 15:48:54 +0100 Subject: [PATCH 097/399] Inverted boolean --- src/components/views/elements/Slider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 6712ddd7fd..adb2a6063b 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -82,7 +82,7 @@ export default class Slider extends React.Component { let selection = null; - if (this.props.disabled) { + if (!this.props.disabled) { const offset = this.offset(this.props.values, this.props.value); selection =
From 4e6748416c3d68757588e563d24f516dc17880c6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 15:53:12 +0100 Subject: [PATCH 098/399] Fix i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bff1b8f415..58226595f7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -405,7 +405,6 @@ "Support adding custom themes": "Support adding custom themes", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", - "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", "Font size": "Font size", "Min font size": "Min font size", "Max font size": "Max font size", From 132a753deb787bb626b2431b7f0434debc3c1b74 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 28 Apr 2020 15:55:26 +0100 Subject: [PATCH 099/399] Lint getDefaultValue --- src/settings/SettingsStore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index b6856a5a6a..688925de40 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -373,9 +373,10 @@ export default class SettingsStore { /** * Gets the default value of a setting. * @param {string} settingName The name of the setting to read the value of. + * @param {String} roomId The room ID to read the setting value in, may be null. * @return {*} The default value */ - static getDefaultValue(settingName, roomId = null, excludeDefault = false) { + static getDefaultValue(settingName) { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); From 2acb1663eb67473738511c3b6aa22899c9344cb3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 01:01:56 +0100 Subject: [PATCH 100/399] Appease the prop types --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 6c94a82c95..d089b4f6e0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -274,8 +274,8 @@ export default class AppearanceUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={this.state.fontSize} - value={this.state.fontSize} + placeholder={toString(this.state.fontSize)} + value={toString(this.state.fontSize)} id="font_size_field" onValidate={this._onValidateFontSize} onChange={({value}) => this.setState({fontSize: value})} From f7b3662e0b306d66feefe7ac03211165518565f7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 10:32:05 +0100 Subject: [PATCH 101/399] Fully appease prop types --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index d089b4f6e0..6fd44b691d 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -107,7 +107,6 @@ export default class AppearanceUserSettingsTab extends React.Component { _onValidateFontSize = ({value}) => { console.log({value}); - this.setState({fontSize: value}); const parsedSize = parseFloat(value); const min = SettingsStore.getValue("font_size_min"); @@ -274,11 +273,11 @@ export default class AppearanceUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={toString(this.state.fontSize)} - value={toString(this.state.fontSize)} + placeholder={this.state.fontSize.toString()} + value={this.state.fontSize.toString()} id="font_size_field" onValidate={this._onValidateFontSize} - onChange={({value}) => this.setState({fontSize: value})} + onChange={(value) => this.setState({fontSize: value.target.value})} disabled={!this.state.useCustomFontSize} />
; From bab7d5f461a6c51d142fe9ff7d5be6cd4cfd9bbb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 6 May 2020 17:25:54 +0100 Subject: [PATCH 102/399] Some lints --- res/css/views/elements/_Slider.scss | 12 ++++++------ res/themes/light/css/_light.scss | 4 ++-- src/FontWatcher.js | 12 ++++++------ src/components/views/elements/Slider.tsx | 2 +- .../tabs/user/AppearanceUserSettingsTab.js | 14 +++++++------- src/settings/Settings.js | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index f6982865db..09afb58b12 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -38,7 +38,7 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2em solid $Slider-background-color; + border: 0.2em solid $slider-background-color; } .mx_Slider_selection { @@ -54,7 +54,7 @@ limitations under the License. position: absolute; width: 1.1em; height: 1.1em; - background-color: $Slider-selection-color; + background-color: $slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; z-index: 10; @@ -63,7 +63,7 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2em solid $Slider-selection-color; + border: 0.2em solid $slider-selection-color; } .mx_Slider_dot { @@ -71,19 +71,19 @@ limitations under the License. height: 1em; width: 1em; border-radius: 50%; - background-color: $Slider-background-color; + background-color: $slider-background-color; z-index: 0; } .mx_Slider_dotActive { - background-color: $Slider-selection-color; + background-color: $slider-selection-color; } .mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; - color: $Slider-background-color; + color: $slider-background-color; } // The following is a hack to center the labels without adding diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index ed7eae48f7..78fe2a74c5 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -263,8 +263,8 @@ $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; // Slider -$Slider-selection-color: $accent-color; -$Slider-background-color: #c1c9d6; +$slider-selection-color: $accent-color; +$slider-background-color: #c1c9d6; $progressbar-color: #000; diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 2e37921ee6..561edc4662 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -23,7 +23,7 @@ export class FontWatcher { } start() { - this._setRootFontSize(SettingsStore.getValue("font_size")); + this._setRootFontSize(SettingsStore.getValue("fontSize")); this._dispatcherRef = dis.register(this._onAction); } @@ -37,15 +37,15 @@ export class FontWatcher { } }; - _setRootFontSize = size => { - const min = SettingsStore.getValue("font_size_min"); - const max = SettingsStore.getValue("font_size_max"); + _setRootFontSize = (size) => { + const min = SettingsStore.getValue("fontSizeMin"); + const max = SettingsStore.getValue("fontSizeMax"); const fontSize = Math.max(Math.min(max, size), min); if (fontSize != size) { - SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); + SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; - } + }; } diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index adb2a6063b..e181f0d9e3 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -47,7 +47,7 @@ export default class Slider extends React.Component { // the index of the first number greater than value. let closest = values.reduce((prev, curr) => { return (value > curr ? prev + 1 : prev); - }, 0); + }, 0); // Off the left if (closest === 0) { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 6fd44b691d..ac98664be0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -32,7 +32,7 @@ export default class AppearanceUserSettingsTab extends React.Component { super(); this.state = { - fontSize: SettingsStore.getValue("font_size", null), + fontSize: SettingsStore.getValue("fontSize", null), ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, @@ -102,15 +102,15 @@ export default class AppearanceUserSettingsTab extends React.Component { _onFontSizeChanged = (size) => { this.setState({fontSize: size}); - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, size); + SettingsStore.setValue("fontSize", null, SettingLevel.DEVICE, size); }; _onValidateFontSize = ({value}) => { console.log({value}); const parsedSize = parseFloat(value); - const min = SettingsStore.getValue("font_size_min"); - const max = SettingsStore.getValue("font_size_max"); + const min = SettingsStore.getValue("fontSizeMin"); + const max = SettingsStore.getValue("fontSizeMax"); if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; @@ -123,7 +123,7 @@ export default class AppearanceUserSettingsTab extends React.Component { }; } - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); + SettingsStore.setValue("fontSize", null, SettingLevel.DEVICE, value); return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})}; } @@ -253,8 +253,8 @@ export default class AppearanceUserSettingsTab extends React.Component {
Aa
Date: Wed, 29 Apr 2020 14:57:45 +0100 Subject: [PATCH 103/399] add useIRCLayout setting --- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a24b2bde73..3dcba0f546 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -408,6 +408,7 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", + "Use IRC layout": "Use IRC layout", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 5c6d843349..c51bf44ee5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -94,6 +94,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "feature_alternate_message_layouts": { + isFeature: true, + displayName: _td("Alternate message layouts"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), @@ -164,6 +170,11 @@ export const SETTINGS = { default: true, invertedSettingName: 'MessageComposerInput.dontSuggestEmoji', }, + "useIRCLayout": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Use IRC layout'), + default: false, + }, "useCompactLayout": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Use compact timeline layout'), From e0c89f6180331350485ca7331ff9e190a83a3d5f Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 15:06:38 +0100 Subject: [PATCH 104/399] Add switch between layout classes --- src/components/structures/MessagePanel.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6fbfdb504b..0123d43920 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -117,6 +117,7 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), + useIRCLayout: SettingsStore.getValue("useIRCLayout"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -169,6 +170,8 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); + + this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange); } componentDidMount() { @@ -178,6 +181,7 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); + SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -196,6 +200,12 @@ export default class MessagePanel extends React.Component { }); }; + onLayoutChange = () => { + this.setState({ + useIRCLayout: SettingsStore.getValue("useIRCLayout"), + }); + } + /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -779,6 +789,8 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, }, ); From c1e740a59603ae178364cac18c8ecdd5443c9df6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 15:07:17 +0100 Subject: [PATCH 105/399] Break out group layout settings --- res/css/views/rooms/_EventTile.scss | 12 ------- res/css/views/rooms/_GroupLayout.scss | 52 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 res/css/views/rooms/_GroupLayout.scss diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 752cf982f6..b9a41c4310 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -68,11 +68,9 @@ limitations under the License. display: inline-block; /* anti-zalgo, with overflow hidden */ overflow: hidden; cursor: pointer; - padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; - line-height: $font-17px; /* the next three lines, along with overflow hidden, truncate long display names */ white-space: nowrap; text-overflow: ellipsis; @@ -101,12 +99,9 @@ limitations under the License. .mx_EventTile .mx_MessageTimestamp { display: block; - visibility: hidden; white-space: nowrap; left: 0px; - width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; - position: absolute; user-select: none; } @@ -117,10 +112,7 @@ limitations under the License. .mx_EventTile_line, .mx_EventTile_reply { position: relative; padding-left: 65px; /* left gutter */ - padding-top: 3px; - padding-bottom: 3px; border-radius: 4px; - line-height: $font-22px; } .mx_RoomView_timeline_rr_enabled, @@ -151,10 +143,6 @@ limitations under the License. margin-right: 10px; } -.mx_EventTile_info .mx_EventTile_line { - padding-left: 83px; -} - /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { font-size: 48px !important; diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss new file mode 100644 index 0000000000..6528d6c6cd --- /dev/null +++ b/res/css/views/rooms/_GroupLayout.scss @@ -0,0 +1,52 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2020 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. +*/ + +$left-gutter: 65px; + +.mx_GroupLayout { + + .mx_EventTile { + > .mx_SenderProfile { + line-height: $font-17px; + padding-left: $left-gutter; + } + + > .mx_EventTile_line { + padding-left: $left-gutter; + } + + > .mx_EventTile_avatar { + position: absolute; + } + + .mx_MessageTimestamp { + visibility: hidden; + position: absolute; + width: 46px; /* 8 + 30 (avatar) + 8 */ + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 3px; + padding-bottom: 3px; + line-height: $font-22px; + } + } + + .mx_EventTile_info .mx_EventTile_line { + padding-left: 83px; + } +} From 10c8d253c86334e11c3a8582db6d63e073bcb79e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 15:07:41 +0100 Subject: [PATCH 106/399] Create irc layout --- res/css/views/rooms/_IRCLayout.scss | 124 ++++++++++++++++++++++ src/components/structures/MessagePanel.js | 1 + src/components/views/rooms/EventTile.js | 30 ++++-- 3 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 res/css/views/rooms/_IRCLayout.scss diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss new file mode 100644 index 0000000000..6152749573 --- /dev/null +++ b/res/css/views/rooms/_IRCLayout.scss @@ -0,0 +1,124 @@ +/* +Copyright 2020 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. +*/ + +$name-width: 50px; +$icon-width: 14px; +$timestamp-width: 45px; +$right-padding: 5px; + +.mx_IRCLayout { + + line-height: $font-22px !important; + + .mx_EventTile { + display: flex; + flex-direction: row; + align-items: flex-start; + + > * { + margin-right: $right-padding; + } + + > .mx_EventTile_msgOption { + order: 4; + flex-shrink: 0; + } + + > .mx_SenderProfile { + order: 2; + flex-shrink: 0; + width: $name-width; + text-overflow: ellipsis; + text-align: right; + display: flex; + align-items: center; + } + + > .mx_EventTile_line { + order: 3; + flex-grow: 1; + } + + > .mx_EventTile_avatar { + order: 1; + position: relative; + top: 0; + left: 0; + flex-shrink: 0; + height: 22px; + display: flex; + align-items: center; + + > .mx_BaseAvatar { + height: 1rem; + width: 1rem; + } + } + + .mx_MessageTimestamp { + font-size: $font-10px; + width: $timestamp-width; + text-align: right; + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding: 0; + } + + .mx_EventTile_e2eIcon { + position: relative; + right: unset; + left: unset; + top: -2px; + padding: 0; + } + + .mx_EventTile_line > * { + display: inline-block; + } + } + + .mx_EventListSummary { + > .mx_EventTile_line { + padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + } + } + + .mx_EventTile.mx_EventTile_info { + .mx_EventTile_avatar { + left: calc($name-width + 10px + $icon-width); + top: 0; + } + + .mx_EventTile_line { + left: calc($name-width + 10px + $icon-width); + } + + .mx_TextualEvent { + line-height: $font-22px; + } + } + + .mx_EventTile_continuation:not(.mx_EventTile_info) { + .mx_EventTile_avatar { + visibility: hidden; + } + + .mx_SenderProfile { + visibility: hidden; + } + } +} diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 0123d43920..80e5d17bf3 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -607,6 +607,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} + useIRCLayout={this.state.useIRCLayout} /> , diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index a64fd82eb5..2da9677a17 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -206,6 +206,9 @@ export default createReactClass({ // whether to show reactions for this event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }, getDefaultProps: function() { @@ -653,6 +656,8 @@ export default createReactClass({ const classes = classNames({ mx_EventTile_bubbleContainer: isBubbleMessage, mx_EventTile: true, + mx_EventTile_irc: this.props.useIRCLayout, + mx_EventTile_group: !this.props.useIRCLayout, mx_EventTile_isEditing: isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, @@ -696,6 +701,9 @@ export default createReactClass({ // joins/parts/etc avatarSize = 14; needsSenderProfile = false; + } else if (this.props.useIRCLayout) { + avatarSize = 14; + needsSenderProfile = true; } else if (this.props.continuation && this.props.tileShape !== "file_grid") { // no avatar or sender profile for continuation messages avatarSize = 0; @@ -879,21 +887,29 @@ export default createReactClass({ this.props.permalinkCreator, this._replyThread, ); + + const linkedTimestamp = + { timestamp } + ; + + const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; + const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; + + // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return (
+ { ircTimestamp }
{ readAvatars }
{ sender }
- - { timestamp } - + { groupTimestamp } { !isBubbleMessage && this._renderE2EPadlock() } { thread } Date: Wed, 29 Apr 2020 15:07:47 +0100 Subject: [PATCH 107/399] Include new css files --- res/css/_components.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/_components.scss b/res/css/_components.scss index 0ba2b609e8..a66d15af18 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -159,6 +159,8 @@ @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_EventTile_group.scss"; +@import "./views/rooms/_EventTile_irc.scss"; @import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; From 6c3e3161de314b109810db0987f04877e5f42a10 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 29 Apr 2020 15:29:25 +0100 Subject: [PATCH 108/399] Reduce padding --- res/css/views/rooms/_IRCLayout.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 6152749573..94ff681029 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -27,6 +27,7 @@ $right-padding: 5px; display: flex; flex-direction: row; align-items: flex-start; + padding-top: 0; > * { margin-right: $right-padding; From 54d211a847c8f198c2547c0cc9fc6c9715d142e5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 4 May 2020 21:40:52 +0100 Subject: [PATCH 109/399] Index file name changes --- res/css/_components.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/_components.scss b/res/css/_components.scss index a66d15af18..5466b785c0 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -159,8 +159,8 @@ @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; -@import "./views/rooms/_EventTile_group.scss"; -@import "./views/rooms/_EventTile_irc.scss"; +@import "./views/rooms/_GroupLayout.scss"; +@import "./views/rooms/_IRCLayout.scss"; @import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; From 67249e1e9c17c9886812e1e2e07a71e298d2bb9e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 4 May 2020 21:53:48 +0100 Subject: [PATCH 110/399] Fix hover --- res/css/views/rooms/_IRCLayout.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 94ff681029..10d8c701c3 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -122,4 +122,12 @@ $right-padding: 5px; visibility: hidden; } } + + // Suppress highlight thing from the normal Layout. + .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { + padding-left: 0; + border-left: 0; + } } From 027826c2e1ca45cda9588a8d5cee257581048987 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 5 May 2020 10:54:44 +0100 Subject: [PATCH 111/399] Replies have the same layout as messages --- res/css/views/rooms/_IRCLayout.scss | 13 +++++++-- src/components/views/elements/ReplyThread.js | 9 ++++-- src/components/views/rooms/EventTile.js | 29 ++++++++++---------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 10d8c701c3..c7cf2c31c2 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -63,9 +63,12 @@ $right-padding: 5px; display: flex; align-items: center; - > .mx_BaseAvatar { - height: 1rem; - width: 1rem; + // Need to use important to override the js provided height and width values. + > .mx_BaseAvatar, .mx_BaseAvatar > * { + height: $font-14px !important; + width: $font-14px !important; + font-size: $font-10px !important; + line-height: $font-14px !important; } } @@ -90,6 +93,10 @@ $right-padding: 5px; .mx_EventTile_line > * { display: inline-block; } + + .mx_EventTile_reply { + order: 3; + } } .mx_EventListSummary { diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index eae2d13f8a..a8f9599f35 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -37,6 +37,8 @@ export default class ReplyThread extends React.Component { // called when the ReplyThread contents has changed, including EventTiles thereof onHeightChanged: PropTypes.func.isRequired, permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, + // Specifies which layout to use. + useIRCLayout: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -176,12 +178,12 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { return
; } return ; + ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} />; } componentDidMount() { @@ -331,7 +333,8 @@ export default class ReplyThread extends React.Component { onHeightChanged={this.props.onHeightChanged} permalinkCreator={this.props.permalinkCreator} isRedacted={ev.isRedacted()} - isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} /> + isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} + useIRCLayout={this.props.useIRCLayout} /> ; }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 2da9677a17..837c8929b9 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -795,6 +795,17 @@ export default createReactClass({ />; } + const linkedTimestamp = + { timestamp } + ; + + const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; + const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; + switch (this.props.tileShape) { case 'notif': { const room = this.context.getRoom(this.props.mxEvent.getRoomId()); @@ -862,12 +873,11 @@ export default createReactClass({ } return (
+ { ircTimestamp } { avatar } { sender }
- - { timestamp } - + { groupTimestamp } { !isBubbleMessage && this._renderE2EPadlock() } { thread } - { timestamp } - ; - - const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; - const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; - - // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return (
From 0af265bf93e703c42492c51b41a23f485f4099fe Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 5 May 2020 16:24:50 +0100 Subject: [PATCH 112/399] Fix replies --- res/css/views/rooms/_IRCLayout.scss | 9 ++++++++- src/components/views/elements/ReplyThread.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index c7cf2c31c2..b45d34013c 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -90,10 +90,17 @@ $right-padding: 5px; padding: 0; } - .mx_EventTile_line > * { + .mx_EventTile_line .mx_EventTile_content, + .mx_EventTile_line .mx_EventTile_e2eIcon, + .mx_eventTile_line > div { display: inline-block; } + .mx_EvenTile_line .mx_MessageActionBar, + .mx_EvenTile_line .mx_ReplyThread_wrapper { + display: block; + } + .mx_EventTile_reply { order: 3; } diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index a8f9599f35..52a94110ba 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -338,7 +338,7 @@ export default class ReplyThread extends React.Component { ; }); - return
+ return
{ header }
{ evTiles }
; From 07c2d0cb0256ff7ab359416222d6411e0100594a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 6 May 2020 09:24:33 +0100 Subject: [PATCH 113/399] Composer reply previews have group layout --- src/components/views/rooms/MessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4749742a7d..663db5e942 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -370,7 +370,7 @@ export default class MessageComposer extends React.Component { } return ( -
+
{ controls } From 5568e6488d18c2ef8da79514f852ac562cdcf049 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 6 May 2020 09:51:01 +0100 Subject: [PATCH 114/399] Fix encryption badge layout --- res/css/views/rooms/_IRCLayout.scss | 2 +- src/components/views/elements/ReplyThread.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index b45d34013c..968d8ebdea 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -92,7 +92,7 @@ $right-padding: 5px; .mx_EventTile_line .mx_EventTile_content, .mx_EventTile_line .mx_EventTile_e2eIcon, - .mx_eventTile_line > div { + .mx_EventTile_line .mx_ReplyThread_wrapper_empty { display: inline-block; } diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 52a94110ba..de242f5632 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -180,7 +180,7 @@ export default class ReplyThread extends React.Component { static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { - return
; + return
; } return ; From 771ae5e18f23b7b7954950b43fa7e5e52b70b0e8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 7 May 2020 13:55:23 +0100 Subject: [PATCH 115/399] Fix encryption badge layouts and replies. Begin removing dependence on slider. Move settings to labs. Username disambiguation. --- res/css/views/rooms/_IRCLayout.scss | 34 +++++++++++++++---- src/components/structures/MessagePanel.js | 16 +++++++-- src/components/views/elements/ReplyThread.js | 10 ++++-- .../views/messages/SenderProfile.js | 3 ++ src/components/views/rooms/EventTile.js | 6 +++- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 17 +++++++--- 7 files changed, 69 insertions(+), 18 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 968d8ebdea..e4536aec20 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -$name-width: 50px; +$name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; .mx_IRCLayout { - line-height: $font-22px !important; + line-height: $font-20px !important; .mx_EventTile { display: flex; @@ -46,11 +46,13 @@ $right-padding: 5px; text-align: right; display: flex; align-items: center; + overflow: visible; } > .mx_EventTile_line { order: 3; flex-grow: 1; + margin-bottom: -6px; } > .mx_EventTile_avatar { @@ -90,10 +92,13 @@ $right-padding: 5px; padding: 0; } - .mx_EventTile_line .mx_EventTile_content, - .mx_EventTile_line .mx_EventTile_e2eIcon, - .mx_EventTile_line .mx_ReplyThread_wrapper_empty { - display: inline-block; + .mx_EventTile_line { + .mx_EventTile_e2eIcon, + .mx_TextualEvent, + .mx_MTextBody, + .mx_ReplyThread_wrapper_empty { + display: inline-block; + } } .mx_EvenTile_line .mx_MessageActionBar, @@ -104,6 +109,10 @@ $right-padding: 5px; .mx_EventTile_reply { order: 3; } + + .mx_EditMessageComposer_buttons { + position: relative; + } } .mx_EventListSummary { @@ -144,4 +153,17 @@ $right-padding: 5px; padding-left: 0; border-left: 0; } + + .mx_SenderProfile_hover { + background-color: $primary-bg-color; + overflow: hidden; + width: $name-width; + transition: width 2s; + } + + .mx_SenderProfile_hover:hover { + overflow: visible; + width: auto; + z-index: 10; + } } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 80e5d17bf3..66ed5ee81f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -117,7 +117,8 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -171,7 +172,8 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); - this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange); + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); + this._displayAvatarsWatcherRef = SettingsStore.watchSetting("feature_no_timeline_avatars", null, this.onDisplayAvatarsChange); } componentDidMount() { @@ -182,6 +184,7 @@ export default class MessagePanel extends React.Component { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); SettingsStore.unwatchSetting(this._layoutWatcherRef); + SettingsStore.unwatchSetting(this._displayAvatarsWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -202,7 +205,13 @@ export default class MessagePanel extends React.Component { onLayoutChange = () => { this.setState({ - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + }); + } + + onDisplayAvatarsChange = () => { + this.setState({ + displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }); } @@ -608,6 +617,7 @@ export default class MessagePanel extends React.Component { getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} useIRCLayout={this.state.useIRCLayout} + displayAvatars={this.state.displayAvatars} /> , diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index de242f5632..d1d46e709a 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -39,6 +39,8 @@ export default class ReplyThread extends React.Component { permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, // Specifies which layout to use. useIRCLayout: PropTypes.bool, + // Specifies whether to display avatars. + displayAvatars: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -178,12 +180,12 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout, displayAvatars) { if (!ReplyThread.getParentEventId(parentEv)) { return
; } return ; + ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} displayAvatars={displayAvatars} />; } componentDidMount() { @@ -334,7 +336,9 @@ export default class ReplyThread extends React.Component { permalinkCreator={this.props.permalinkCreator} isRedacted={ev.isRedacted()} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} - useIRCLayout={this.props.useIRCLayout} /> + useIRCLayout={this.props.useIRCLayout} + displayAvatars={this.props.displayAvatars} + /> ; }); diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index bed93b68c3..d95c9d685a 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -131,7 +131,10 @@ export default createReactClass({ return (
+
+ { content } { content } +
); }, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 837c8929b9..b2daa5667f 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -209,6 +209,9 @@ export default createReactClass({ // whether to use the irc layout useIRCLayout: PropTypes.bool, + + // whether to display avatars + displayAvatars: PropTypes.bool, }, getDefaultProps: function() { @@ -713,7 +716,7 @@ export default createReactClass({ needsSenderProfile = true; } - if (this.props.mxEvent.sender && avatarSize) { + if (this.props.mxEvent.sender && avatarSize && this.props.displayAvatars) { avatar = (
Date: Thu, 7 May 2020 14:06:40 +0100 Subject: [PATCH 116/399] Remove unused setting --- src/settings/Settings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 7942aa67fc..032b0ee906 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -94,12 +94,6 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, - "feature_alternate_message_layouts": { - isFeature: true, - displayName: _td("Alternate message layouts"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), From bc5fc57dd667d89fd774949cb2b940d198518a90 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 7 May 2020 14:22:15 +0100 Subject: [PATCH 117/399] Lint This is why we shouldn't rely on regex --- src/components/structures/MessagePanel.js | 6 +++++- src/components/views/elements/ReplyThread.js | 9 +++++++-- src/i18n/strings/en_EN.json | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 66ed5ee81f..f46df0175a 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -173,7 +173,11 @@ export default class MessagePanel extends React.Component { SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); - this._displayAvatarsWatcherRef = SettingsStore.watchSetting("feature_no_timeline_avatars", null, this.onDisplayAvatarsChange); + this._displayAvatarsWatcherRef = SettingsStore.watchSetting( + "feature_no_timeline_avatars", + null, + this.onDisplayAvatarsChange, + ); } componentDidMount() { diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index d1d46e709a..6bfda5dd94 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -184,8 +184,13 @@ export default class ReplyThread extends React.Component { if (!ReplyThread.getParentEventId(parentEv)) { return
; } - return ; + return ; } componentDidMount() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3cd88f9ae..ca62eb44fa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -405,11 +405,11 @@ "Multiple integration managers": "Multiple integration managers", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Support adding custom themes": "Support adding custom themes", + "Use IRC layout": "Use IRC layout", + "Display user avatars on messages": "Display user avatars on messages", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", - "Use IRC layout": "Use IRC layout", - "Display user avatars on messages": "Display user avatars on messages", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", From ac95172ed4e6981bc5615fb982176d4a8c17a866 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 7 May 2020 14:56:45 +0100 Subject: [PATCH 118/399] tighter layout --- res/css/views/rooms/_IRCLayout.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index e4536aec20..fcdeef6590 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -21,7 +21,7 @@ $right-padding: 5px; .mx_IRCLayout { - line-height: $font-20px !important; + line-height: $font-18px !important; .mx_EventTile { display: flex; @@ -52,7 +52,6 @@ $right-padding: 5px; > .mx_EventTile_line { order: 3; flex-grow: 1; - margin-bottom: -6px; } > .mx_EventTile_avatar { From 9b7c63a7116c369d3e3e59c3a643067b6666ebca Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Fri, 8 May 2020 20:53:32 +0100 Subject: [PATCH 119/399] Duplicated names --- src/components/views/messages/SenderProfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index d95c9d685a..d512b186e9 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -133,7 +133,6 @@ export default createReactClass({
{ content } - { content }
); From 82396661cf86d7c41285cded2d22523e84f3bc23 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Fri, 8 May 2020 22:21:26 +0100 Subject: [PATCH 120/399] Implement nitpicks - usernames are elipsed - icon alignment fixed - replies are more dense - reply messages respond to name widths - fixed between message padding problem (flex ftw) --- res/css/views/rooms/_IRCLayout.scss | 62 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index fcdeef6590..f5d8664884 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -18,12 +18,19 @@ $name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; +$irc-line-height: $font-18px; .mx_IRCLayout { - line-height: $font-18px !important; + line-height: $irc-line-height !important; .mx_EventTile { + + // timestamps are links which shouldn't be underlined + > a { + text-decoration: none; + } + display: flex; flex-direction: row; align-items: flex-start; @@ -49,7 +56,10 @@ $right-padding: 5px; overflow: visible; } - > .mx_EventTile_line { + .mx_EventTile_line, .mx_EventTile_reply { + padding: 0; + display: flex; + flex-direction: column; order: 3; flex-grow: 1; } @@ -60,7 +70,7 @@ $right-padding: 5px; top: 0; left: 0; flex-shrink: 0; - height: 22px; + height: $irc-line-height; display: flex; align-items: center; @@ -79,10 +89,6 @@ $right-padding: 5px; text-align: right; } - .mx_EventTile_line, .mx_EventTile_reply { - padding: 0; - } - .mx_EventTile_e2eIcon { position: relative; right: unset; @@ -98,6 +104,8 @@ $right-padding: 5px; .mx_ReplyThread_wrapper_empty { display: inline-block; } + + } .mx_EvenTile_line .mx_MessageActionBar, @@ -114,10 +122,25 @@ $right-padding: 5px; } } + .mx_EventTile_emote { + > .mx_EventTile_avatar { + margin-left: calc($name-width + $icon-width + $right-padding); + } + } + + blockquote { + margin: 0; + } + .mx_EventListSummary { > .mx_EventTile_line { padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding } + + .mx_EventListSummary_avatars { + padding: 0; + margin: 0; + } } .mx_EventTile.mx_EventTile_info { @@ -131,16 +154,16 @@ $right-padding: 5px; } .mx_TextualEvent { - line-height: $font-22px; + line-height: $irc-line-height; } } .mx_EventTile_continuation:not(.mx_EventTile_info) { - .mx_EventTile_avatar { + > .mx_EventTile_avatar { visibility: hidden; } - .mx_SenderProfile { + > .mx_SenderProfile { visibility: hidden; } } @@ -156,8 +179,15 @@ $right-padding: 5px; .mx_SenderProfile_hover { background-color: $primary-bg-color; overflow: hidden; - width: $name-width; - transition: width 2s; + + > span { + display: flex; + + > .mx_SenderProfile_name { + overflow: hidden; + text-overflow: ellipsis; + } + } } .mx_SenderProfile_hover:hover { @@ -165,4 +195,12 @@ $right-padding: 5px; width: auto; z-index: 10; } + + .mx_ReplyThread { + margin: 0; + .mx_SenderProfile { + width: unset; + max-width: $name-width; + } + } } From fef4d882c44dcf93b677bac50cc09097df7f7839 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Fri, 8 May 2020 22:35:40 +0100 Subject: [PATCH 121/399] lint --- res/css/views/rooms/_IRCLayout.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index f5d8664884..301f712ffb 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -104,8 +104,6 @@ $irc-line-height: $font-18px; .mx_ReplyThread_wrapper_empty { display: inline-block; } - - } .mx_EvenTile_line .mx_MessageActionBar, From 5029c3f1434b33fe8b4aae825f5f3e03d4425dc0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 02:16:43 +0100 Subject: [PATCH 122/399] Implement IRC draggable display name width --- res/css/views/rooms/_IRCLayout.scss | 23 +++-- src/components/structures/MessagePanel.js | 7 ++ src/components/structures/ScrollPanel.js | 6 ++ src/components/views/elements/Draggable.tsx | 84 ++++++++++++++++++ .../views/elements/ErrorBoundary.js | 2 +- .../elements/IRCTimelineProfileResizer.tsx | 86 +++++++++++++++++++ src/settings/Settings.js | 7 ++ 7 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 src/components/views/elements/Draggable.tsx create mode 100644 src/components/views/elements/IRCTimelineProfileResizer.tsx diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 301f712ffb..159cfc0aad 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -$name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; $irc-line-height: $font-18px; .mx_IRCLayout { + --name-width: 70px; line-height: $irc-line-height !important; @@ -48,7 +48,7 @@ $irc-line-height: $font-18px; > .mx_SenderProfile { order: 2; flex-shrink: 0; - width: $name-width; + width: var(--name-width); text-overflow: ellipsis; text-align: right; display: flex; @@ -122,7 +122,7 @@ $irc-line-height: $font-18px; .mx_EventTile_emote { > .mx_EventTile_avatar { - margin-left: calc($name-width + $icon-width + $right-padding); + margin-left: calc(var(--name-width) + $icon-width + $right-padding); } } @@ -132,7 +132,7 @@ $irc-line-height: $font-18px; .mx_EventListSummary { > .mx_EventTile_line { - padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + padding-left: calc(var(--name-width) + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding } .mx_EventListSummary_avatars { @@ -143,12 +143,12 @@ $irc-line-height: $font-18px; .mx_EventTile.mx_EventTile_info { .mx_EventTile_avatar { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); top: 0; } .mx_EventTile_line { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); } .mx_TextualEvent { @@ -198,7 +198,16 @@ $irc-line-height: $font-18px; margin: 0; .mx_SenderProfile { width: unset; - max-width: $name-width; + max-width: var(--name-width); } } + + .mx_ProfileResizer { + position: absolute; + height: 100%; + width: 15px; + left: calc(80px + var(--name-width)); + cursor: col-resize; + z-index: 100; + } } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index f46df0175a..1c10efb346 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -29,6 +29,7 @@ import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; import {textForEvent} from "../../TextForEvent"; +import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -819,6 +820,11 @@ export default class MessagePanel extends React.Component { ); } + let ircResizer = null; + if (this.state.useIRCLayout) { + ircResizer = ; + } + return ( { topSpinner } { this._getEventTiles() } diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 4f44c1a169..cb0114b243 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -144,6 +144,11 @@ export default createReactClass({ /* resizeNotifier: ResizeNotifier to know when middle column has changed size */ resizeNotifier: PropTypes.object, + + /* fixedChildren: allows for children to be passed which are rendered outside + * of the wrapper + */ + fixedChildren: PropTypes.node, }, getDefaultProps: function() { @@ -881,6 +886,7 @@ export default createReactClass({ return ( + { this.props.fixedChildren }
    { this.props.children } diff --git a/src/components/views/elements/Draggable.tsx b/src/components/views/elements/Draggable.tsx new file mode 100644 index 0000000000..98f86fd524 --- /dev/null +++ b/src/components/views/elements/Draggable.tsx @@ -0,0 +1,84 @@ +/* +Copyright 2020 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'; + +interface IProps { + className: string, + dragFunc: (currentLocation: ILocationState, event: MouseEvent) => ILocationState, + onMouseUp: (event: MouseEvent) => void, +} + +interface IState { + onMouseMove: (event: MouseEvent) => void, + onMouseUp: (event: MouseEvent) => void, + location: ILocationState, +} + +export interface ILocationState { + currentX: number, + currentY: number, +} + +export default class Draggable extends React.Component { + + constructor(props: IProps) { + super(props); + + this.state = { + onMouseMove: this.onMouseMove.bind(this), + onMouseUp: this.onMouseUp.bind(this), + location: { + currentX: 0, + currentY: 0, + }, + }; + } + + private onMouseDown = (event: MouseEvent): void => { + this.setState({ + location: { + currentX: event.clientX, + currentY: event.clientY, + }, + }); + + document.addEventListener("mousemove", this.state.onMouseMove); + document.addEventListener("mouseup", this.state.onMouseUp); + console.log("Mouse down") + } + + private onMouseUp = (event: MouseEvent): void => { + document.removeEventListener("mousemove", this.state.onMouseMove); + document.removeEventListener("mouseup", this.state.onMouseUp); + this.props.onMouseUp(event); + console.log("Mouse up") + } + + private onMouseMove(event: MouseEvent): void { + console.log("Mouse Move") + const newLocation = this.props.dragFunc(this.state.location, event); + + this.setState({ + location: newLocation, + }); + } + + render() { + return
    + } + +} \ No newline at end of file diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index a043b350ab..1abd11f838 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -73,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; - return
    + return

    {_t("Something went wrong!")}

    {_t( diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx new file mode 100644 index 0000000000..80a86b2005 --- /dev/null +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -0,0 +1,86 @@ +/* +Copyright 2020 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 SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import Draggable, {ILocationState} from './Draggable'; + +interface IProps { + // Current room + roomId: string, + minWidth: number, + maxWidth: number, +}; + +interface IState { + width: number, + IRCLayoutRoot: HTMLElement, +}; + +export default class IRCTimelineProfileResizer extends React.Component { + constructor(props: IProps) { + super(props); + + this.state = { + width: SettingsStore.getValue("ircDisplayNameWidth", this.props.roomId), + IRCLayoutRoot: null, + } + }; + + componentDidMount() { + this.setState({ + IRCLayoutRoot: document.querySelector(".mx_IRCLayout") as HTMLElement, + }, () => this.updateCSSWidth(this.state.width)) + } + + private dragFunc = (location: ILocationState, event: React.MouseEvent): ILocationState => { + const offset = event.clientX - location.currentX; + const newWidth = this.state.width + offset; + + console.log({offset}) + // If we're trying to go smaller than min width, don't. + if (this.state.width <= this.props.minWidth && offset <= 0) { + return location; + } + + if (this.state.width >= this.props.maxWidth && offset >= 0) { + return location; + } + + this.setState({ + width: newWidth, + }); + + this.updateCSSWidth.bind(this)(newWidth); + + return { + currentX: event.clientX, + currentY: location.currentY, + } + } + + private updateCSSWidth(newWidth: number) { + this.state.IRCLayoutRoot.style.setProperty("--name-width", newWidth + "px"); + } + + private onMoueUp(event: MouseEvent) { + SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); + } + + render() { + return + } +}; \ No newline at end of file diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 032b0ee906..6d741ba3a5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -531,4 +531,11 @@ export const SETTINGS = { MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, ), }, + "ircDisplayNameWidth": { + // We specifically want to have room-device > device so that users may set a device default + // with a per-room override. + supportedLevels: ['room-device', 'device'], + displayName: _td("IRC display name width"), + default: 80, + }, }; From 3f04f5163a963b9193199abb340719061ae0e921 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 14:04:46 +0100 Subject: [PATCH 123/399] Implement more nitpicks - fix avatar inital aligment - right align names - set flair height to avatar's - fix conditions for resizing to be more stable --- res/css/views/rooms/_IRCLayout.scss | 13 ++++++++++++- .../views/elements/IRCTimelineProfileResizer.tsx | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 159cfc0aad..8d48c72f8a 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -54,6 +54,7 @@ $irc-line-height: $font-18px; display: flex; align-items: center; overflow: visible; + justify-content: flex-end; } .mx_EventTile_line, .mx_EventTile_reply { @@ -79,7 +80,7 @@ $irc-line-height: $font-18px; height: $font-14px !important; width: $font-14px !important; font-size: $font-10px !important; - line-height: $font-14px !important; + line-height: $font-15px !important; } } @@ -188,6 +189,10 @@ $irc-line-height: $font-18px; } } + .mx_SenderProfile:hover { + justify-content: flex-start; + } + .mx_SenderProfile_hover:hover { overflow: visible; width: auto; @@ -210,4 +215,10 @@ $irc-line-height: $font-18px; cursor: col-resize; z-index: 100; } + + // Need to use important to override the js provided height and width values. + .mx_Flair > img { + height: $font-14px !important; + width: $font-14px !important; + } } diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 80a86b2005..44ceeb9b7b 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -52,11 +52,11 @@ export default class IRCTimelineProfileResizer extends React.Component= this.props.maxWidth && offset >= 0) { + if (newWidth > this.props.maxWidth) { return location; } From 312b616d7701e9b9f8460d210bc22a07c1253be9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 14:11:16 +0100 Subject: [PATCH 124/399] fix i18n --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ca62eb44fa..37b9c1dfc8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -453,6 +453,7 @@ "Keep recovery passphrase in memory for this session": "Keep recovery passphrase in memory for this session", "How fast should messages be downloaded.": "How fast should messages be downloaded.", "Manually verify all remote sessions": "Manually verify all remote sessions", + "IRC display name width": "IRC display name width", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", From 328bb7bcaf05621443c7a75ab791a9c8eb0ac884 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 15:24:08 +0100 Subject: [PATCH 125/399] Remove all animations --- res/css/_common.scss | 1 - res/css/views/elements/_Slider.scss | 3 --- 2 files changed, 4 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index 687a238c8e..03442ca510 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,7 +19,6 @@ limitations under the License. @import "./_font-sizes.scss"; :root { - transition: font-size 0.25s; font-size: 15px; } diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 09afb58b12..06c3c4c98b 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -50,7 +50,6 @@ limitations under the License. } .mx_Slider_selectionDot { - transition: left 0.25s; position: absolute; width: 1.1em; height: 1.1em; @@ -61,13 +60,11 @@ limitations under the License. } .mx_Slider_selection > hr { - transition: width 0.25s; margin: 0; border: 0.2em solid $slider-selection-color; } .mx_Slider_dot { - transition: background-color 0.2s ease-in; height: 1em; width: 1em; border-radius: 50%; From fea219915f4a666d73259406323b185a9d5067f6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 15:26:11 +0100 Subject: [PATCH 126/399] fix code regeression --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 0cee29233f..21e406aa23 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -60,9 +60,6 @@ export default class GeneralUserSettingsTab extends React.Component { emails: [], msisdns: [], loading3pids: true, // whether or not the emails and msisdns have been loaded - ...this._calculateThemeState(), - customThemeUrl: "", - customThemeMessage: {isError: false, text: ""}, }; this.dispatcherRef = dis.register(this._onAction); From 20ec900405721dce6faf130a20223ff4faa01ff6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 15:36:53 +0100 Subject: [PATCH 127/399] Set font range --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 6 +----- src/settings/Settings.js | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ac98664be0..1ccc744dc7 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -252,11 +252,7 @@ export default class AppearanceUserSettingsTab extends React.Component {

    Aa
    {}} diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 34610c0caf..afe8d2cecc 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -174,12 +174,12 @@ export const SETTINGS = { "fontSizeMin": { displayName: _td("Min font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 14, + default: 13, }, "fontSizeMax": { displayName: _td("Max font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 24, + default: 20, }, "useCustomFontSize": { displayName: _td("Custom font size"), From 5c2abcf1a4b7bd9cbc35dd6c297073db56eb8a52 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 17:05:37 +0100 Subject: [PATCH 128/399] Show username on continuations --- res/css/views/rooms/_IRCLayout.scss | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 8d48c72f8a..f2a616f9c9 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -157,16 +157,6 @@ $irc-line-height: $font-18px; } } - .mx_EventTile_continuation:not(.mx_EventTile_info) { - > .mx_EventTile_avatar { - visibility: hidden; - } - - > .mx_SenderProfile { - visibility: hidden; - } - } - // Suppress highlight thing from the normal Layout. .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, From fc6e5227aced6f801bc1f1ffa3c7cfc86d84ef6d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 13 May 2020 22:08:29 +0100 Subject: [PATCH 129/399] FIx roomsublist heights. - also fiddles the font size numbers --- res/css/structures/_TagPanel.scss | 2 +- res/css/views/rooms/_RoomTile.scss | 3 ++- src/components/structures/RoomSubList.js | 4 ++-- src/components/views/avatars/BaseAvatar.js | 20 +++++++++---------- src/components/views/rooms/EventTile.js | 2 +- .../views/rooms/ReadReceiptMarker.js | 2 +- .../tabs/user/AppearanceUserSettingsTab.js | 2 +- src/utils/{rem.js => units.ts} | 9 ++++++++- 8 files changed, 26 insertions(+), 18 deletions(-) rename src/utils/{rem.js => units.ts} (77%) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 536c88be63..1f8443e395 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -69,7 +69,7 @@ limitations under the License. height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { - height: $font-40px; + height: 40px; padding: 10px 0 9px 0; } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 5bc7d5624d..759dce5afa 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: $font-34px; + height: 32px; margin: 0; padding: 0 8px 0 10px; position: relative; @@ -81,6 +81,7 @@ limitations under the License. .mx_RoomTile_avatar_container { position: relative; + display: flex; } .mx_RoomTile_avatar { diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index c8f9ba1713..245b5f9bea 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,7 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; -import {toRem} from "../../utils/rem"; +import {toPx} from "../../utils/units"; // turn this on for drop & drag console debugging galore const debug = false; @@ -420,7 +420,7 @@ export default class RoomSubList extends React.PureComponent { setHeight = (height) => { if (this._subList.current) { - this._subList.current.style.height = toRem(height); + this._subList.current.style.height = toPx(height); } this._updateLazyRenderHeight(height); }; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index a9bee9cda0..704e6438c8 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,7 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import {toRem} from "../../../utils/rem"; +import {toPx} from "../../../utils/units"; export default createReactClass({ displayName: 'BaseAvatar', @@ -166,9 +166,9 @@ export default createReactClass({ const textNode = (
    + >{sublists}
    )} ); diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx new file mode 100644 index 0000000000..a3ca525b14 --- /dev/null +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -0,0 +1,61 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017, 2018 Vector Creations Ltd +Copyright 2020 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 * as React from "react"; +import { Room } from "matrix-js-sdk/src/models/room"; + +interface IProps { + forRooms: boolean; + rooms?: Room[]; + startAsHidden: boolean; + label: string; + onAddRoom?: () => void; + addRoomLabel: string; + isInvite: boolean; + + // TODO: Collapsed state + // TODO: Height + // TODO: Group invites + // TODO: Calls + // TODO: forceExpand? + // TODO: Header clicking + // TODO: Spinner support for historical +} + +interface IState { +} + +// TODO: Actually write stub +export class RoomSublist2 extends React.Component { + public setHeight(size: number) { + } + + public render() { + // TODO: Proper rendering + + const rooms = this.props.rooms.map(r => ( +
    {r.name} ({r.roomId})
    + )); + return ( +
    +

    {this.props.label}

    + {rooms} +
    + ); + } +} From 5dda7f02cffb88e0621b68429fcc725e4dab8bb6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 May 2020 09:06:34 -0600 Subject: [PATCH 151/399] Early handling of dispatched events A possible approach to handling the various triggers for recategorizing rooms. --- src/components/views/rooms/RoomList2.tsx | 1 - src/stores/room-list/RoomListStore2.ts | 41 ++++++++++++++++--- .../algorithms/list_ordering/Algorithm.ts | 41 +++++++++++-------- .../list_ordering/ChaoticAlgorithm.ts | 7 +--- .../list_ordering/ImportanceAlgorithm.ts | 17 ++++---- src/stores/room-list/models.ts | 5 +++ 6 files changed, 75 insertions(+), 37 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 04cb8a4549..f7788f0003 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -28,7 +28,6 @@ import { Dispatcher } from "flux"; import { ActionPayload } from "../../../dispatcher-types"; import dis from "../../../dispatcher"; import { RoomSublist2 } from "./RoomSublist2"; -import { isNullOrUndefined } from "matrix-js-sdk/lib/src/utils"; interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 0b3f61e261..06f97ae53e 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -18,12 +18,14 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/client"; import { ActionPayload, defaultDispatcher } from "../../dispatcher-types"; import SettingsStore from "../../settings/SettingsStore"; -import { DefaultTagID, OrderedDefaultTagIDs, TagID } from "./models"; +import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models"; import { Algorithm } from "./algorithms/list_ordering/Algorithm"; import TagOrderStore from "../TagOrderStore"; import { getListAlgorithmInstance } from "./algorithms/list_ordering"; import { AsyncStore } from "../AsyncStore"; import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; interface IState { tagsEnabled?: boolean; @@ -123,7 +125,14 @@ class _RoomListStore extends AsyncStore { await this.regenerateAllLists(); // regenerate the lists now } - } else if (payload.action === 'MatrixActions.Room.receipt') { + } + + if (!this.algorithm) { + // This shouldn't happen because `initialListsGenerated` implies we have an algorithm. + throw new Error("Room list store has no algorithm to process dispatcher update with"); + } + + if (payload.action === 'MatrixActions.Room.receipt') { // First see if the receipt event is for our own user. If it was, trigger // a room update (we probably read the room on a different device). // noinspection JSObjectNullOrUndefined - this.matrixClient can't be null by this point in the lifecycle @@ -132,23 +141,45 @@ class _RoomListStore extends AsyncStore { const receiptUsers = Object.keys(payload.event.getContent()[eventId]['m.read'] || {}); if (receiptUsers.includes(myUserId)) { // TODO: Update room now that it's been read + console.log(payload); return; } } } else if (payload.action === 'MatrixActions.Room.tags') { // TODO: Update room from tags - } else if (payload.action === 'MatrixActions.room.timeline') { - // TODO: Update room from new events + console.log(payload); + } else if (payload.action === 'MatrixActions.Room.timeline') { + const eventPayload = (payload); // TODO: Type out the dispatcher types + + // Ignore non-live events (backfill) + if (!eventPayload.isLiveEvent || !payload.isLiveUnfilteredRoomTimelineEvent) return; + + const roomId = eventPayload.event.getRoomId(); + const room = this.matrixClient.getRoom(roomId); + await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); } else if (payload.action === 'MatrixActions.Event.decrypted') { // TODO: Update room from decrypted event + console.log(payload); } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { // TODO: Update DMs + console.log(payload); } else if (payload.action === 'MatrixActions.Room.myMembership') { // TODO: Update room from membership change - } else if (payload.action === 'MatrixActions.room') { + console.log(payload); + } else if (payload.action === 'MatrixActions.Room') { // TODO: Update room from creation/join + console.log(payload); } else if (payload.action === 'view_room') { // TODO: Update sticky room + console.log(payload); + } + } + + private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { + const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); + if (shouldUpdate) { + console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`); + this.emit(LISTS_UPDATE_EVENT, this); } } diff --git a/src/stores/room-list/algorithms/list_ordering/Algorithm.ts b/src/stores/room-list/algorithms/list_ordering/Algorithm.ts index fd98f34966..e154847847 100644 --- a/src/stores/room-list/algorithms/list_ordering/Algorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/Algorithm.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { DefaultTagID, TagID } from "../../models"; +import { DefaultTagID, RoomUpdateCause, TagID } from "../../models"; import { Room } from "matrix-js-sdk/src/models/room"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import { EffectiveMembership, splitRoomsByMembership } from "../../membership"; @@ -33,9 +33,8 @@ export abstract class Algorithm { protected cached: ITagMap = {}; protected sortAlgorithms: ITagSortingMap; protected rooms: Room[] = []; - protected roomsByTag: { - // @ts-ignore - TS wants this to be a string but we know better - [tagId: TagID]: Room[]; + protected roomIdsToTags: { + [roomId: string]: TagID[]; } = {}; protected constructor() { @@ -132,6 +131,25 @@ export abstract class Algorithm { await this.generateFreshTags(newTags); this.cached = newTags; + this.updateTagsFromCache(); + } + + /** + * Updates the roomsToTags map + */ + protected updateTagsFromCache() { + const newMap = {}; + + const tags = Object.keys(this.cached); + for (const tagId of tags) { + const rooms = this.cached[tagId]; + for (const room of rooms) { + if (!newMap[room.roomId]) newMap[room.roomId] = []; + newMap[room.roomId].push(tagId); + } + } + + this.roomIdsToTags = newMap; } /** @@ -144,27 +162,16 @@ export abstract class Algorithm { */ protected abstract generateFreshTags(updatedTagMap: ITagMap): Promise; - /** - * Called when the Algorithm wants a whole tag to be reordered. Typically this will - * be done whenever the tag's scope changes (added/removed rooms). - * @param {TagID} tagId The tag ID which changed. - * @param {Room[]} rooms The rooms within the tag, unordered. - * @returns {Promise} Resolves to the ordered rooms in the tag. - */ - // TODO: Do we need this? - protected abstract regenerateTag(tagId: TagID, rooms: Room[]): Promise; - /** * Asks the Algorithm to update its knowledge of a room. For example, when * a user tags a room, joins/creates a room, or leaves a room the Algorithm * should be told that the room's info might have changed. The Algorithm * may no-op this request if no changes are required. * @param {Room} room The room which might have affected sorting. + * @param {RoomUpdateCause} cause The reason for the update being triggered. * @returns {Promise} A promise which resolve to true or false * depending on whether or not getOrderedRooms() should be called after * processing. */ - // TODO: Take a ReasonForChange to better predict the behaviour? - // TODO: Intercept here and handle tag changes automatically? May be best to let the impl do that. - public abstract handleRoomUpdate(room: Room): Promise; + public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise; } diff --git a/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts index 7c1a0b1acc..185fb606fb 100644 --- a/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts @@ -15,7 +15,6 @@ limitations under the License. */ import { Algorithm } from "./Algorithm"; -import { DefaultTagID } from "../../models"; import { ITagMap } from "../models"; /** @@ -33,11 +32,7 @@ export class ChaoticAlgorithm extends Algorithm { return Promise.resolve(); } - protected async regenerateTag(tagId: string | DefaultTagID, rooms: []): Promise<[]> { - return Promise.resolve(rooms); - } - - public async handleRoomUpdate(room): Promise { + public async handleRoomUpdate(room, cause): Promise { return Promise.resolve(false); } } diff --git a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts index d73fdee930..d66f7cdc37 100644 --- a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts @@ -17,9 +17,9 @@ limitations under the License. import { Algorithm } from "./Algorithm"; import { Room } from "matrix-js-sdk/src/models/room"; -import { DefaultTagID, TagID } from "../../models"; +import { DefaultTagID, RoomUpdateCause, TagID } from "../../models"; import { ITagMap, SortAlgorithm } from "../models"; -import { getSortingAlgorithmInstance, sortRoomsWithAlgorithm } from "../tag_sorting"; +import { sortRoomsWithAlgorithm } from "../tag_sorting"; import * as Unread from '../../../../Unread'; /** @@ -194,11 +194,12 @@ export class ImportanceAlgorithm extends Algorithm { } } - protected async regenerateTag(tagId: string | DefaultTagID, rooms: []): Promise<[]> { - return Promise.resolve(rooms); - } - - public async handleRoomUpdate(room): Promise { - return Promise.resolve(false); + public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { + const tags = this.roomIdsToTags[room.roomId]; + if (!tags) { + console.warn(`No tags known for "${room.name}" (${room.roomId})`); + return false; + } + return false; } } diff --git a/src/stores/room-list/models.ts b/src/stores/room-list/models.ts index d1c915e035..5294680773 100644 --- a/src/stores/room-list/models.ts +++ b/src/stores/room-list/models.ts @@ -34,3 +34,8 @@ export const OrderedDefaultTagIDs = [ ]; export type TagID = string | DefaultTagID; + +export enum RoomUpdateCause { + Timeline = "TIMELINE", + RoomRead = "ROOM_READ", +} From ea34bb3022cda77e96a67e6ae1f06ef881db24d2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 May 2020 09:13:35 -0600 Subject: [PATCH 152/399] Make component index happy --- src/components/views/rooms/RoomList2.tsx | 2 +- src/components/views/rooms/RoomSublist2.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index f7788f0003..7aefc21a49 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -27,7 +27,7 @@ import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { Dispatcher } from "flux"; import { ActionPayload } from "../../../dispatcher-types"; import dis from "../../../dispatcher"; -import { RoomSublist2 } from "./RoomSublist2"; +import RoomSublist2 from "./RoomSublist2"; interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index a3ca525b14..ed3740e8b3 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -41,7 +41,7 @@ interface IState { } // TODO: Actually write stub -export class RoomSublist2 extends React.Component { +export default class RoomSublist2 extends React.Component { public setHeight(size: number) { } From e1fab9a5b663a8c15398dfd15d2015a0b242e193 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 7 May 2020 16:34:22 -0600 Subject: [PATCH 153/399] Work out the new category index for each room update See comments within for details on what this means. --- .../list_ordering/ImportanceAlgorithm.ts | 173 ++++++++++++++---- 1 file changed, 136 insertions(+), 37 deletions(-) diff --git a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts index d66f7cdc37..76cdf4763f 100644 --- a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts @@ -17,7 +17,7 @@ limitations under the License. import { Algorithm } from "./Algorithm"; import { Room } from "matrix-js-sdk/src/models/room"; -import { DefaultTagID, RoomUpdateCause, TagID } from "../../models"; +import { RoomUpdateCause, TagID } from "../../models"; import { ITagMap, SortAlgorithm } from "../models"; import { sortRoomsWithAlgorithm } from "../tag_sorting"; import * as Unread from '../../../../Unread'; @@ -51,6 +51,16 @@ interface ICategorizedRoomMap { [category: Category]: Room[]; } +interface ICategoryIndex { + // @ts-ignore - TS wants this to be a string, but we know better + [category: Category]: number; // integer +} + +// Caution: changing this means you'll need to update a bunch of assumptions and +// comments! Check the usage of Category carefully to figure out what needs changing +// if you're going to change this array's order. +const CATEGORY_ORDER = [Category.Red, Category.Grey, Category.Bold, Category.Idle]; + /** * An implementation of the "importance" algorithm for room list sorting. Where * the tag sorting algorithm does not interfere, rooms will be ordered into @@ -69,6 +79,7 @@ interface ICategorizedRoomMap { */ export class ImportanceAlgorithm extends Algorithm { + // TODO: Update documentation // HOW THIS WORKS // -------------- // @@ -80,7 +91,7 @@ export class ImportanceAlgorithm extends Algorithm { // those tags are tracked as index numbers within the array (zero = top), with // each sticky room being tracked separately. Internally, the category index // can be found from `this.indices[tag][category]` and the sticky room information - // from `this.stickyRooms[tag]`. + // from `this.stickyRoom`. // // Room categories are constantly re-evaluated and tracked in the `this.categorized` // object. Note that this doesn't track rooms by category but instead by room ID. @@ -93,33 +104,17 @@ export class ImportanceAlgorithm extends Algorithm { private indices: { // @ts-ignore - TS wants this to be a string but we know better than it - [tag: TagID]: { - // @ts-ignore - TS wants this to be a string but we know better than it - [category: Category]: number; // integer - }; - } = {}; - private stickyRooms: { - // @ts-ignore - TS wants this to be a string but we know better than it - [tag: TagID]: { - room?: Room; - nAbove: number; // integer - }; - } = {}; - private categorized: { - // @ts-ignore - TS wants this to be a string but we know better than it - [tag: TagID]: { - // TODO: Remove note - // Note: Should in theory be able to only track this by room ID as we'll know - // the indices of each category and can determine if a category needs changing - // in the cached list. Could potentially save a bunch of time if we can figure - // out where a room is supposed to be using offsets, some math, and leaving the - // array generally alone. - [roomId: string]: { - room: Room; - category: Category; - }; - }; + [tag: TagID]: ICategoryIndex; } = {}; + private stickyRoom: { + roomId: string; + tag: TagID; + fromTop: number; + } = { + roomId: null, + tag: null, + fromTop: 0, + }; constructor() { super(); @@ -136,7 +131,6 @@ export class ImportanceAlgorithm extends Algorithm { }; for (const room of rooms) { const category = this.getRoomCategory(room); - console.log(`[DEBUG] "${room.name}" (${room.roomId}) is a ${category} room`); map[category].push(room); } return map; @@ -183,13 +177,16 @@ export class ImportanceAlgorithm extends Algorithm { categorized[category] = await sortRoomsWithAlgorithm(roomsToOrder, tagId, sortBy); } - // TODO: Update positions of categories in cache - updatedTagMap[tagId] = [ - ...categorized[Category.Red], - ...categorized[Category.Grey], - ...categorized[Category.Bold], - ...categorized[Category.Idle], - ]; + const newlyOrganized: Room[] = []; + const newIndicies: ICategoryIndex = {}; + + for (const category of CATEGORY_ORDER) { + newIndicies[category] = newlyOrganized.length; + newlyOrganized.push(...categorized[category]); + } + + this.indices[tagId] = newIndicies; + updatedTagMap[tagId] = newlyOrganized; } } } @@ -200,6 +197,108 @@ export class ImportanceAlgorithm extends Algorithm { console.warn(`No tags known for "${room.name}" (${room.roomId})`); return false; } - return false; + const category = this.getRoomCategory(room); + let changed = false; + for (const tag of tags) { + if (this.sortAlgorithms[tag] === SortAlgorithm.Manual) { + continue; // Nothing to do here. + } + + const taggedRooms = this.cached[tag]; + const indicies = this.indices[tag]; + let roomIdx = taggedRooms.indexOf(room); + let inList = true; + if (roomIdx === -1) { + console.warn(`Degrading performance to find missing room in "${tag}": ${room.roomId}`); + roomIdx = taggedRooms.findIndex(r => r.roomId === room.roomId); + } + if (roomIdx === -1) { + console.warn(`Room ${room.roomId} has no index in ${tag} - assuming end of list`); + roomIdx = taggedRooms.length; + inList = false; // used so we don't splice the dead room out + } + + // Try to avoid doing array operations if we don't have to: only move rooms within + // the categories if we're jumping categories + const oldCategory = this.getCategoryFromIndicies(roomIdx, indicies); + if (oldCategory !== category) { + // Move the room and update the indicies + this.moveRoomIndexes(1, oldCategory, category, indicies); + taggedRooms.splice(roomIdx, 1); // splice out the old index (fixed position) + taggedRooms.splice(indicies[category], 0, room); // splice in the new room (pre-adjusted) + // Note: if moveRoomIndexes() is called after the splice then the insert operation + // will happen in the wrong place. Because we would have already adjusted the index + // for the category, we don't need to determine how the room is moving in the list. + // If we instead tried to insert before updating the indicies, we'd have to determine + // whether the room was moving later (towards IDLE) or earlier (towards RED) from its + // current position, as it'll affect the category's start index after we remove the + // room from the array. + } + + // The room received an update, so take out the slice and sort it. This should be relatively + // quick because the room is inserted at the top of the category, and most popular sorting + // algorithms will deal with trying to keep the active room at the top/start of the category. + // For the few algorithms that will have to move the thing quite far (alphabetic with a Z room + // for example), the list should already be sorted well enough that it can rip through the + // array and slot the changed room in quickly. + const nextCategoryStartIdx = category === CATEGORY_ORDER[CATEGORY_ORDER.length - 1] + ? Number.MAX_SAFE_INTEGER + : indicies[CATEGORY_ORDER[CATEGORY_ORDER.indexOf(category) + 1]]; + const startIdx = indicies[category]; + const numSort = nextCategoryStartIdx - startIdx; // splice() returns up to the max, so MAX_SAFE_INT is fine + const unsortedSlice = taggedRooms.splice(startIdx, numSort); + const sorted = await sortRoomsWithAlgorithm(unsortedSlice, tag, this.sortAlgorithms[tag]); + taggedRooms.splice(startIdx, 0, ...sorted); + + // Finally, flag that we've done something + changed = true; + } + return changed; + } + + private getCategoryFromIndicies(index: number, indicies: ICategoryIndex): Category { + for (let i = 0; i < CATEGORY_ORDER.length; i++) { + const category = CATEGORY_ORDER[i]; + const isLast = i === (CATEGORY_ORDER.length - 1); + const startIdx = indicies[category]; + const endIdx = isLast ? Number.MAX_SAFE_INTEGER : indicies[CATEGORY_ORDER[i + 1]]; + if (index >= startIdx && index < endIdx) { + return category; + } + } + + // "Should never happen" disclaimer goes here + throw new Error("Programming error: somehow you've ended up with an index that isn't in a category"); + } + + private moveRoomIndexes(nRooms: number, fromCategory: Category, toCategory: Category, indicies: ICategoryIndex) { + // We have to update the index of the category *after* the from/toCategory variables + // in order to update the indicies correctly. Because the room is moving from/to those + // categories, the next category's index will change - not the category we're modifying. + // We also need to update subsequent categories as they'll all shift by nRooms, so we + // loop over the order to achieve that. + + for (let i = CATEGORY_ORDER.indexOf(fromCategory) + 1; i < CATEGORY_ORDER.length; i++) { + const nextCategory = CATEGORY_ORDER[i]; + indicies[nextCategory] -= nRooms; + } + + for (let i = CATEGORY_ORDER.indexOf(toCategory) + 1; i < CATEGORY_ORDER.length; i++) { + const nextCategory = CATEGORY_ORDER[i]; + indicies[nextCategory] += nRooms; + } + + // Do a quick check to see if we've completely broken the index + for (let i = 1; i <= CATEGORY_ORDER.length; i++) { + const lastCat = CATEGORY_ORDER[i - 1]; + const thisCat = CATEGORY_ORDER[i]; + + if (indicies[lastCat] > indicies[thisCat]) { + // "should never happen" disclaimer goes here + console.warn(`!! Room list index corruption: ${lastCat} (i:${indicies[lastCat]}) is greater than ${thisCat} (i:${indicies[thisCat]}) - category indicies are likely desynced from reality`); + + // TODO: Regenerate index when this happens + } + } } } From 4a0d14e32269fa8e4049d06173e7d9c4035a6499 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 7 May 2020 16:38:14 -0600 Subject: [PATCH 154/399] Make missing rooms throw instead For now at least. We shouldn't encounter this case until we get around to adding support for newly-joined rooms. --- .../algorithms/list_ordering/ImportanceAlgorithm.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts index 76cdf4763f..0ebdad1ed1 100644 --- a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts @@ -207,15 +207,12 @@ export class ImportanceAlgorithm extends Algorithm { const taggedRooms = this.cached[tag]; const indicies = this.indices[tag]; let roomIdx = taggedRooms.indexOf(room); - let inList = true; if (roomIdx === -1) { console.warn(`Degrading performance to find missing room in "${tag}": ${room.roomId}`); roomIdx = taggedRooms.findIndex(r => r.roomId === room.roomId); } if (roomIdx === -1) { - console.warn(`Room ${room.roomId} has no index in ${tag} - assuming end of list`); - roomIdx = taggedRooms.length; - inList = false; // used so we don't splice the dead room out + throw new Error(`Room ${room.roomId} has no index in ${tag}`); } // Try to avoid doing array operations if we don't have to: only move rooms within From e88788f4e9c7804c9b78bcd670035bcfe14fae18 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 8 May 2020 11:59:03 -0600 Subject: [PATCH 155/399] Handle event decryption too --- src/stores/room-list/RoomListStore2.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 06f97ae53e..3a6d911dde 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -156,10 +156,21 @@ class _RoomListStore extends AsyncStore { const roomId = eventPayload.event.getRoomId(); const room = this.matrixClient.getRoom(roomId); + console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()} in ${roomId}`); await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); } else if (payload.action === 'MatrixActions.Event.decrypted') { - // TODO: Update room from decrypted event - console.log(payload); + const eventPayload = (payload); // TODO: Type out the dispatcher types + const roomId = eventPayload.event.getRoomId(); + const room = this.matrixClient.getRoom(roomId); + if (!room) { + console.warn(`Event ${eventPayload.event.getId()} was decrypted in an unknown room ${roomId}`); + return; + } + console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`); + // TODO: Check that e2e rooms are calculated correctly on initial load. + // It seems like when viewing the room the timeline is decrypted, rather than at startup. This could + // cause inaccuracies with the list ordering. We may have to decrypt the last N messages of every room :( + await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { // TODO: Update DMs console.log(payload); From cb3d17ee28de8d0e96650b8f1dfb5a6fa698a388 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 8 May 2020 12:53:05 -0600 Subject: [PATCH 156/399] Bare minimum for rendering a room list This is non-interactive and missing most features users will expect to have --- src/components/views/rooms/RoomList2.tsx | 9 +- src/components/views/rooms/RoomSublist2.tsx | 174 +++++++++++++++++++- src/components/views/rooms/RoomTile2.tsx | 118 +++++++++++++ 3 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 src/components/views/rooms/RoomTile2.tsx diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 7aefc21a49..9b0d4579f0 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -156,7 +156,7 @@ export default class RoomList2 extends React.Component { const sublist = this.sublistRefs[tagId]; if (sublist) sublist.current.setHeight(height); - // TODO: Check overflow + // TODO: Check overflow (see old impl) // Don't store a height for collapsed sublists if (!this.sublistCollapseStates[tagId]) { @@ -178,6 +178,7 @@ export default class RoomList2 extends React.Component { } private collectSublistRef(tagId: string, ref: React.RefObject) { + // TODO: Is this needed? if (!ref) { delete this.sublistRefs[tagId]; } else { @@ -206,11 +207,9 @@ export default class RoomList2 extends React.Component { const aesthetics: ITagAesthetics = TAG_AESTHETICS[orderedTagId]; if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`); - const onAddRoomFn = () => { - if (!aesthetics.onAddRoom) return; - aesthetics.onAddRoom(dis); - }; + const onAddRoomFn = aesthetics.onAddRoom ? () => aesthetics.onAddRoom(dis) : null; components.push( { + private headerButton = createRef(); + public setHeight(size: number) { + // TODO: Do a thing } - public render() { - // TODO: Proper rendering + private hasTiles(): boolean { + return this.numTiles > 0; + } + + private get numTiles(): number { + // TODO: Account for group invites + return (this.props.rooms || []).length; + } + + private onAddRoom = (e) => { + e.stopPropagation(); + if (this.props.onAddRoom) this.props.onAddRoom(); + }; + + private renderTiles(): React.ReactElement[] { + const tiles: React.ReactElement[] = []; + + if (this.props.rooms) { + for (const room of this.props.rooms) { + tiles.push(); + } + } + + return tiles; + } + + private renderHeader(): React.ReactElement { + const notifications = !this.props.isInvite + ? RoomNotifs.aggregateNotificationCount(this.props.rooms) + : {count: 0, highlight: true}; + const notifCount = notifications.count; + const notifHighlight = notifications.highlight; + + // TODO: Title on collapsed + // TODO: Incoming call box + + let chevron = null; + if (this.hasTiles()) { + const chevronClasses = classNames({ + 'mx_RoomSubList_chevron': true, + 'mx_RoomSubList_chevronRight': false, // isCollapsed + 'mx_RoomSubList_chevronDown': true, // !isCollapsed + }); + chevron = (
    ); + } - const rooms = this.props.rooms.map(r => ( -
    {r.name} ({r.roomId})
    - )); return ( -
    -

    {this.props.label}

    - {rooms} + + {({onFocus, isActive, ref}) => { + const tabIndex = isActive ? 0 : -1; + + let badge; + if (true) { // !isCollapsed + const badgeClasses = classNames({ + 'mx_RoomSubList_badge': true, + 'mx_RoomSubList_badgeHighlight': notifHighlight, + }); + // Wrap the contents in a div and apply styles to the child div so that the browser default outline works + if (notifCount > 0) { + badge = ( + +
    + {FormattingUtils.formatCount(notifCount)} +
    +
    + ); + } else if (this.props.isInvite && this.hasTiles()) { + // Render the `!` badge for invites + badge = ( + +
    + {FormattingUtils.formatCount(this.numTiles)} +
    +
    + ); + } + } + + let addRoomButton = null; + if (!!this.props.onAddRoom) { + addRoomButton = ( + + ); + } + + // TODO: a11y + return ( +
    + + {chevron} + {this.props.label} + + {badge} + {addRoomButton} +
    + ); + }} +
    + ); + } + + public render(): React.ReactElement { + // TODO: Proper rendering + // TODO: Error boundary + + const tiles = this.renderTiles(); + + const classes = classNames({ + // TODO: Proper collapse support + 'mx_RoomSubList': true, + 'mx_RoomSubList_hidden': false, // len && isCollapsed + 'mx_RoomSubList_nonEmpty': this.hasTiles(), // len && !isCollapsed + }); + + let content = null; + if (tiles.length > 0) { + // TODO: Lazy list rendering + // TODO: Whatever scrolling magic needs to happen here + content = ( + + {tiles} + + ) + } + + // TODO: onKeyDown support + return ( +
    + {this.renderHeader()} + {content}
    ); } diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx new file mode 100644 index 0000000000..cb9ce5cf1a --- /dev/null +++ b/src/components/views/rooms/RoomTile2.tsx @@ -0,0 +1,118 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 New Vector Ltd +Copyright 2018 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019, 2020 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, { createRef } from "react"; +import { Room } from "matrix-js-sdk/src/models/room"; +import classNames from "classnames"; +import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; +import AccessibleButton from "../../views/elements/AccessibleButton"; +import RoomAvatar from "../../views/avatars/RoomAvatar"; + +interface IProps { + room: Room; + + // TODO: Allow faslifying counts (for invites and stuff) + // TODO: Transparency? + // TODO: Incoming call? + // TODO: onClick +} + +interface IState { +} + +// TODO: Finish stub +export default class RoomTile2 extends React.Component { + private roomTile = createRef(); + + // TODO: Custom status + // TODO: Lock icon + // TODO: DM indicator + // TODO: Presence indicator + // TODO: e2e shields + // TODO: Handle changes to room aesthetics (name, join rules, etc) + // TODO: scrollIntoView? + // TODO: hover, badge, etc + // TODO: isSelected for hover effects + // TODO: Context menu + // TODO: a11y + + public render(): React.ReactElement { + // TODO: Collapsed state + // TODO: Invites + // TODO: a11y proper + // TODO: Render more than bare minimum + + const classes = classNames({ + 'mx_RoomTile': true, + // 'mx_RoomTile_selected': this.state.selected, + // 'mx_RoomTile_unread': this.props.unread, + // 'mx_RoomTile_unreadNotify': notifBadges, + // 'mx_RoomTile_highlight': mentionBadges, + // 'mx_RoomTile_invited': isInvite, + // 'mx_RoomTile_menuDisplayed': isMenuDisplayed, + 'mx_RoomTile_noBadges': true, // !badges + // 'mx_RoomTile_transparent': this.props.transparent, + // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed, + }); + + const avatarClasses = classNames({ + 'mx_RoomTile_avatar': true, + }); + + // TODO: the original RoomTile uses state for the room name. Do we need to? + let name = this.props.room.name; + if (typeof name !== 'string') name = ''; + name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon + + const nameClasses = classNames({ + 'mx_RoomTile_name': true, + 'mx_RoomTile_invite': false, + 'mx_RoomTile_badgeShown': false, + }); + + return ( + + + {({onFocus, isActive, ref}) => + +
    +
    + +
    +
    +
    +
    +
    + {name} +
    +
    +
    +
    + } +
    +
    + ); + } +} From df3d5c415917324a3784535c23fc695f5e3ff1ff Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 14:25:30 -0600 Subject: [PATCH 157/399] Update i18n for room list --- src/i18n/strings/en_EN.json | 10 +++++----- src/settings/Settings.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fd474f378c..d6ff5f70eb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -406,7 +406,7 @@ "Render simple counters in room header": "Render simple counters in room header", "Multiple integration managers": "Multiple integration managers", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", - "Use the improved room list component (refresh to apply changes)": "Use the improved room list component (refresh to apply changes)", + "Use the improved room list component (refresh to apply changes, in development)": "Use the improved room list component (refresh to apply changes, in development)", "Support adding custom themes": "Support adding custom themes", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", @@ -1117,7 +1117,7 @@ "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", - "TODO": "TODO", + "Create room": "Create room", "This room": "This room", "Joining room …": "Joining room …", "Loading …": "Loading …", @@ -1161,6 +1161,9 @@ "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", "Not now": "Not now", "Don't ask me again": "Don't ask me again", + "Jump to first unread room.": "Jump to first unread room.", + "Jump to first invite.": "Jump to first invite.", + "Add room": "Add room", "Options": "Options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", "%(count)s unread messages including mentions.|one": "1 unread mention.", @@ -2052,9 +2055,6 @@ "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", "Active call": "Active call", "There's no one else here! Would you like to invite others or stop warning about the empty room?": "There's no one else here! Would you like to invite others or stop warning about the empty room?", - "Jump to first unread room.": "Jump to first unread room.", - "Jump to first invite.": "Jump to first invite.", - "Add room": "Add room", "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", "Search failed": "Search failed", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 554cf6b968..110fd4238b 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -133,7 +133,7 @@ export const SETTINGS = { }, "feature_new_room_list": { isFeature: true, - displayName: _td("Use the improved room list component (refresh to apply changes)"), + displayName: _td("Use the improved room list component (refresh to apply changes, in development)"), supportedLevels: LEVELS_FEATURE, default: false, }, From 9f0810240f6ae8d627708d8d51e4f058d0173f55 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 16:12:45 -0600 Subject: [PATCH 158/399] Clean up imports and other minor lints --- src/components/views/rooms/RoomList2.tsx | 2 +- src/stores/room-list/RoomListStore2.ts | 5 ++--- src/stores/room-list/RoomListStoreTempProxy.ts | 4 +--- src/stores/room-list/algorithms/tag_sorting/index.ts | 2 +- src/stores/room-list/membership.ts | 3 +-- src/stores/room-list/models.ts | 1 + 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 9b0d4579f0..402a7af014 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -27,7 +27,7 @@ import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { Dispatcher } from "flux"; import { ActionPayload } from "../../../dispatcher-types"; import dis from "../../../dispatcher"; -import RoomSublist2 from "./RoomSublist2"; +import RoomSublist2 from "./RoomSublist2"; interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 3a6d911dde..8bbcfc3c8d 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -21,11 +21,10 @@ import SettingsStore from "../../settings/SettingsStore"; import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models"; import { Algorithm } from "./algorithms/list_ordering/Algorithm"; import TagOrderStore from "../TagOrderStore"; -import { getListAlgorithmInstance } from "./algorithms/list_ordering"; import { AsyncStore } from "../AsyncStore"; -import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; import { Room } from "matrix-js-sdk/src/models/room"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; +import { getListAlgorithmInstance } from "./algorithms/list_ordering"; interface IState { tagsEnabled?: boolean; diff --git a/src/stores/room-list/RoomListStoreTempProxy.ts b/src/stores/room-list/RoomListStoreTempProxy.ts index 4edca2b9cd..0268cf0a46 100644 --- a/src/stores/room-list/RoomListStoreTempProxy.ts +++ b/src/stores/room-list/RoomListStoreTempProxy.ts @@ -14,13 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { TagID } from "./models"; -import { Room } from "matrix-js-sdk/src/models/room"; import SettingsStore from "../../settings/SettingsStore"; import RoomListStore from "./RoomListStore2"; import OldRoomListStore from "../RoomListStore"; -import { ITagMap } from "./algorithms/models"; import { UPDATE_EVENT } from "../AsyncStore"; +import { ITagMap } from "./algorithms/models"; /** * Temporary RoomListStore proxy. Should be replaced with RoomListStore2 when diff --git a/src/stores/room-list/algorithms/tag_sorting/index.ts b/src/stores/room-list/algorithms/tag_sorting/index.ts index 07f8f484d8..155c0f0118 100644 --- a/src/stores/room-list/algorithms/tag_sorting/index.ts +++ b/src/stores/room-list/algorithms/tag_sorting/index.ts @@ -19,7 +19,7 @@ import { SortAlgorithm } from "../models"; import { ManualAlgorithm } from "./ManualAlgorithm"; import { IAlgorithm } from "./IAlgorithm"; import { TagID } from "../../models"; -import {Room} from "matrix-js-sdk/src/models/room"; +import { Room } from "matrix-js-sdk/src/models/room"; const ALGORITHM_INSTANCES: { [algorithm in SortAlgorithm]: IAlgorithm } = { [SortAlgorithm.Recent]: new ChaoticAlgorithm(), diff --git a/src/stores/room-list/membership.ts b/src/stores/room-list/membership.ts index 884e2a4a04..3cb4bf146c 100644 --- a/src/stores/room-list/membership.ts +++ b/src/stores/room-list/membership.ts @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {Room} from "matrix-js-sdk/src/models/room"; -import {Event} from "matrix-js-sdk/src/models/event"; +import { Room } from "matrix-js-sdk/src/models/room"; /** * Approximation of a membership status for a given room. diff --git a/src/stores/room-list/models.ts b/src/stores/room-list/models.ts index 5294680773..428378a7aa 100644 --- a/src/stores/room-list/models.ts +++ b/src/stores/room-list/models.ts @@ -23,6 +23,7 @@ export enum DefaultTagID { DM = "im.vector.fake.direct", ServerNotice = "m.server_notice", } + export const OrderedDefaultTagIDs = [ DefaultTagID.Invite, DefaultTagID.Favourite, From 715dd7e1b6126ec0f858d80af1873917d9ebe714 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 16:20:26 -0600 Subject: [PATCH 159/399] Prepare tooltip for collapsed support --- src/components/views/rooms/RoomTile2.tsx | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index cb9ce5cf1a..21ba32ae75 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -23,6 +23,7 @@ import classNames from "classnames"; import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomAvatar from "../../views/avatars/RoomAvatar"; +import Tooltip from "../../views/elements/Tooltip"; interface IProps { room: Room; @@ -34,6 +35,7 @@ interface IProps { } interface IState { + hover: boolean; } // TODO: Finish stub @@ -52,6 +54,22 @@ export default class RoomTile2 extends React.Component { // TODO: Context menu // TODO: a11y + constructor(props: IProps) { + super(props); + + this.state = { + hover: false, + }; + } + + private onTileMouseEnter = () => { + this.setState({hover: true}); + }; + + private onTileMouseLeave = () => { + this.setState({hover: false}); + }; + public render(): React.ReactElement { // TODO: Collapsed state // TODO: Invites @@ -86,6 +104,13 @@ export default class RoomTile2 extends React.Component { 'mx_RoomTile_badgeShown': false, }); + let tooltip = null; + if (false) { // isCollapsed + if (this.state.hover) { + tooltip = + } + } + return ( @@ -95,6 +120,8 @@ export default class RoomTile2 extends React.Component { tabIndex={isActive ? 0 : -1} inputRef={ref} className={classes} + onMouseEnter={this.onTileMouseEnter} + onMouseLeave={this.onTileMouseLeave} role="treeitem" >
    @@ -109,6 +136,7 @@ export default class RoomTile2 extends React.Component {
    + {tooltip} } From 6bdcbd0f3d2137caedc19f9659eecac0636632f9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 16:29:32 -0600 Subject: [PATCH 160/399] Support switching rooms --- src/components/views/rooms/RoomTile2.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 21ba32ae75..f32bd7924f 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -24,6 +24,8 @@ import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomAvatar from "../../views/avatars/RoomAvatar"; import Tooltip from "../../views/elements/Tooltip"; +import dis from '../../../dispatcher'; +import { Key } from "../../../Keyboard"; interface IProps { room: Room; @@ -31,7 +33,6 @@ interface IProps { // TODO: Allow faslifying counts (for invites and stuff) // TODO: Transparency? // TODO: Incoming call? - // TODO: onClick } interface IState { @@ -70,6 +71,16 @@ export default class RoomTile2 extends React.Component { this.setState({hover: false}); }; + private onTileClick = (ev: React.KeyboardEvent) => { + dis.dispatch({ + action: 'view_room', + // TODO: Support show_room_tile in new room list + show_room_tile: true, // make sure the room is visible in the list + room_id: this.props.room.roomId, + clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)), + }); + }; + public render(): React.ReactElement { // TODO: Collapsed state // TODO: Invites @@ -122,6 +133,7 @@ export default class RoomTile2 extends React.Component { className={classes} onMouseEnter={this.onTileMouseEnter} onMouseLeave={this.onTileMouseLeave} + onClick={this.onTileClick} role="treeitem" >
    From e8c33161ec3871805403ef0e508cd79321570a8d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 11 May 2020 21:09:32 -0600 Subject: [PATCH 161/399] Initial work on badges This doesn't work for bold rooms --- src/components/views/rooms/RoomTile2.tsx | 68 +++++++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index f32bd7924f..c6c400ce52 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -26,6 +26,10 @@ import RoomAvatar from "../../views/avatars/RoomAvatar"; import Tooltip from "../../views/elements/Tooltip"; import dis from '../../../dispatcher'; import { Key } from "../../../Keyboard"; +import * as RoomNotifs from '../../../RoomNotifs'; +import { EffectiveMembership, getEffectiveMembership } from "../../../stores/room-list/membership"; +import * as Unread from '../../../Unread'; +import * as FormattingUtils from "../../../utils/FormattingUtils"; interface IProps { room: Room; @@ -35,7 +39,14 @@ interface IProps { // TODO: Incoming call? } -interface IState { +interface IBadgeState { + showBadge: boolean; // if numUnread > 0 && !showBadge -> bold room + numUnread: number; // used only if showBadge or showBadgeHighlight is true + showBadgeHighlight: boolean; // make the badge red + isInvite: boolean; // show a `!` instead of a number +} + +interface IState extends IBadgeState { hover: boolean; } @@ -60,6 +71,35 @@ export default class RoomTile2 extends React.Component { this.state = { hover: false, + + ...this.getBadgeState(), + }; + } + + public componentWillUnmount() { + + } + + private updateBadgeCount() { + this.setState({...this.getBadgeState()}); + } + + private getBadgeState(): IBadgeState { + // TODO: Make this code path faster + const highlightCount = RoomNotifs.getUnreadNotificationCount(this.props.room, 'highlight'); + const numUnread = RoomNotifs.getUnreadNotificationCount(this.props.room); + const showBadge = Unread.doesRoomHaveUnreadMessages(this.props.room); + const myMembership = getEffectiveMembership(this.props.room.getMyMembership()); + const isInvite = myMembership === EffectiveMembership.Invite; + const notifState = RoomNotifs.getRoomNotifsState(this.props.room.roomId); + const shouldShowNotifBadge = RoomNotifs.shouldShowNotifBadge(notifState); + const shouldShowHighlightBadge = RoomNotifs.shouldShowMentionBadge(notifState); + + return { + showBadge: (showBadge && shouldShowNotifBadge) || isInvite, + numUnread, + showBadgeHighlight: (highlightCount > 0 && shouldShowHighlightBadge) || isInvite, + isInvite, }; } @@ -90,12 +130,12 @@ export default class RoomTile2 extends React.Component { const classes = classNames({ 'mx_RoomTile': true, // 'mx_RoomTile_selected': this.state.selected, - // 'mx_RoomTile_unread': this.props.unread, - // 'mx_RoomTile_unreadNotify': notifBadges, - // 'mx_RoomTile_highlight': mentionBadges, - // 'mx_RoomTile_invited': isInvite, + 'mx_RoomTile_unread': this.state.numUnread > 0, + 'mx_RoomTile_unreadNotify': this.state.showBadge, + 'mx_RoomTile_highlight': this.state.showBadgeHighlight, + 'mx_RoomTile_invited': this.state.isInvite, // 'mx_RoomTile_menuDisplayed': isMenuDisplayed, - 'mx_RoomTile_noBadges': true, // !badges + 'mx_RoomTile_noBadges': !this.state.showBadge, // 'mx_RoomTile_transparent': this.props.transparent, // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed, }); @@ -104,6 +144,17 @@ export default class RoomTile2 extends React.Component { 'mx_RoomTile_avatar': true, }); + + let badge; + if (this.state.showBadge) { + const badgeClasses = classNames({ + 'mx_RoomTile_badge': true, + 'mx_RoomTile_badgeButton': false, // this.state.badgeHover || isMenuDisplayed + }); + const formattedCount = this.state.isInvite ? `!` : FormattingUtils.formatCount(this.state.numUnread); + badge =
    {formattedCount}
    ; + } + // TODO: the original RoomTile uses state for the room name. Do we need to? let name = this.props.room.name; if (typeof name !== 'string') name = ''; @@ -111,8 +162,8 @@ export default class RoomTile2 extends React.Component { const nameClasses = classNames({ 'mx_RoomTile_name': true, - 'mx_RoomTile_invite': false, - 'mx_RoomTile_badgeShown': false, + 'mx_RoomTile_invite': this.state.isInvite, + 'mx_RoomTile_badgeShown': this.state.showBadge, }); let tooltip = null; @@ -147,6 +198,7 @@ export default class RoomTile2 extends React.Component { {name}
    + {badge}
    {tooltip} From c37352679d72aec70468d541b2f7cd9483406d30 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 09:34:31 -0600 Subject: [PATCH 162/399] Fix bold rooms not bolding --- src/components/views/rooms/RoomTile2.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index c6c400ce52..c4025fdb53 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -42,6 +42,7 @@ interface IProps { interface IBadgeState { showBadge: boolean; // if numUnread > 0 && !showBadge -> bold room numUnread: number; // used only if showBadge or showBadgeHighlight is true + hasUnread: number; // used to make the room bold showBadgeHighlight: boolean; // make the badge red isInvite: boolean; // show a `!` instead of a number } @@ -98,6 +99,7 @@ export default class RoomTile2 extends React.Component { return { showBadge: (showBadge && shouldShowNotifBadge) || isInvite, numUnread, + hasUnread: showBadge, showBadgeHighlight: (highlightCount > 0 && shouldShowHighlightBadge) || isInvite, isInvite, }; @@ -130,7 +132,7 @@ export default class RoomTile2 extends React.Component { const classes = classNames({ 'mx_RoomTile': true, // 'mx_RoomTile_selected': this.state.selected, - 'mx_RoomTile_unread': this.state.numUnread > 0, + 'mx_RoomTile_unread': this.state.numUnread > 0 || this.state.hasUnread, 'mx_RoomTile_unreadNotify': this.state.showBadge, 'mx_RoomTile_highlight': this.state.showBadgeHighlight, 'mx_RoomTile_invited': this.state.isInvite, From f8cbadaba564cf29e06a81261705882f2783c30f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 12:53:00 -0600 Subject: [PATCH 163/399] Clean up comments in skeleton components --- src/components/views/rooms/RoomList2.tsx | 19 ++++++++++--------- src/components/views/rooms/RoomSublist2.tsx | 16 +++++++++++++--- src/components/views/rooms/RoomTile2.tsx | 20 ++++++++++++++------ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 402a7af014..12a0117505 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -29,6 +29,15 @@ import { ActionPayload } from "../../../dispatcher-types"; import dis from "../../../dispatcher"; import RoomSublist2 from "./RoomSublist2"; +/******************************************************************* + * CAUTION * + ******************************************************************* + * This is a work in progress implementation and isn't complete or * + * even useful as a component. Please avoid using it until this * + * warning disappears. * + ******************************************************************* + */ + interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; onFocus: (ev: React.FocusEvent) => void; @@ -152,6 +161,7 @@ export default class RoomList2 extends React.Component { } private prepareLayouts() { + // TODO: Change layout engine for FTUE support this.unfilteredLayout = new Layout((tagId: string, height: number) => { const sublist = this.sublistRefs[tagId]; if (sublist) sublist.current.setHeight(height); @@ -177,15 +187,6 @@ export default class RoomList2 extends React.Component { }); } - private collectSublistRef(tagId: string, ref: React.RefObject) { - // TODO: Is this needed? - if (!ref) { - delete this.sublistRefs[tagId]; - } else { - this.sublistRefs[tagId] = ref; - } - } - private renderSublists(): React.ReactElement[] { const components: React.ReactElement[] = []; diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 21e58abd12..4c3f65b323 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -29,6 +29,15 @@ import AccessibleTooltipButton from "../../views/elements/AccessibleTooltipButto import * as FormattingUtils from '../../../utils/FormattingUtils'; import RoomTile2 from "./RoomTile2"; +/******************************************************************* + * CAUTION * + ******************************************************************* + * This is a work in progress implementation and isn't complete or * + * even useful as a component. Please avoid using it until this * + * warning disappears. * + ******************************************************************* + */ + interface IProps { forRooms: boolean; rooms?: Room[]; @@ -50,12 +59,11 @@ interface IProps { interface IState { } -// TODO: Finish stub export default class RoomSublist2 extends React.Component { private headerButton = createRef(); public setHeight(size: number) { - // TODO: Do a thing + // TODO: Do a thing (maybe - height changes are different in FTUE) } private hasTiles(): boolean { @@ -107,8 +115,10 @@ export default class RoomSublist2 extends React.Component { return ( {({onFocus, isActive, ref}) => { + // TODO: Use onFocus const tabIndex = isActive ? 0 : -1; + // TODO: Collapsed state let badge; if (true) { // !isCollapsed const badgeClasses = classNames({ @@ -156,7 +166,7 @@ export default class RoomSublist2 extends React.Component { ); } - // TODO: a11y + // TODO: a11y (see old component) return (
    { private roomTile = createRef(); // TODO: Custom status // TODO: Lock icon - // TODO: DM indicator // TODO: Presence indicator // TODO: e2e shields // TODO: Handle changes to room aesthetics (name, join rules, etc) @@ -78,7 +85,7 @@ export default class RoomTile2 extends React.Component { } public componentWillUnmount() { - + // TODO: Listen for changes to the badge count and update as needed } private updateBadgeCount() { @@ -168,6 +175,7 @@ export default class RoomTile2 extends React.Component { 'mx_RoomTile_badgeShown': this.state.showBadge, }); + // TODO: Support collapsed state properly let tooltip = null; if (false) { // isCollapsed if (this.state.hover) { From aafbd7f2089446aa0c8a9ffc3e2e131ad7ef2e7d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 13:01:51 -0600 Subject: [PATCH 164/399] Update misc documentation and spell indices correctly --- src/stores/RoomListStore.js | 2 +- src/stores/room-list/RoomListStore2.ts | 4 +- .../list_ordering/ImportanceAlgorithm.ts | 49 +++++++++---------- src/stores/room-list/models.ts | 2 +- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index ccccbcc313..d452d1589e 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -121,7 +121,7 @@ class RoomListStore extends Store { _checkDisabled() { this.disabled = SettingsStore.isFeatureEnabled("feature_new_room_list"); if (this.disabled) { - console.warn("DISABLING LEGACY ROOM LIST STORE"); + console.warn("👋 legacy room list store has been disabled"); } } diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 8bbcfc3c8d..c461aeab66 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -67,7 +67,7 @@ class _RoomListStore extends AsyncStore { private checkEnabled() { this.enabled = SettingsStore.isFeatureEnabled("feature_new_room_list"); if (this.enabled) { - console.log("ENABLING NEW ROOM LIST STORE"); + console.log("⚡ new room list store engaged"); } } @@ -225,7 +225,7 @@ class _RoomListStore extends AsyncStore { } if (this.state.tagsEnabled) { - // TODO: Find a more reliable way to get tags + // TODO: Find a more reliable way to get tags (this doesn't work) const roomTags = TagOrderStore.getOrderedTags() || []; console.log("rtags", roomTags); } diff --git a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts index 0ebdad1ed1..fd5d4c8163 100644 --- a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts @@ -79,7 +79,6 @@ const CATEGORY_ORDER = [Category.Red, Category.Grey, Category.Bold, Category.Idl */ export class ImportanceAlgorithm extends Algorithm { - // TODO: Update documentation // HOW THIS WORKS // -------------- // @@ -93,19 +92,17 @@ export class ImportanceAlgorithm extends Algorithm { // can be found from `this.indices[tag][category]` and the sticky room information // from `this.stickyRoom`. // - // Room categories are constantly re-evaluated and tracked in the `this.categorized` - // object. Note that this doesn't track rooms by category but instead by room ID. - // The theory is that by knowing the previous position, new desired position, and - // category indices we can avoid tracking multiple complicated maps in memory. - // // The room list store is always provided with the `this.cached` results, which are // updated as needed and not recalculated often. For example, when a room needs to // move within a tag, the array in `this.cached` will be spliced instead of iterated. + // The `indices` help track the positions of each category to make splicing easier. private indices: { // @ts-ignore - TS wants this to be a string but we know better than it [tag: TagID]: ICategoryIndex; } = {}; + + // TODO: Use this (see docs above) private stickyRoom: { roomId: string; tag: TagID; @@ -178,14 +175,14 @@ export class ImportanceAlgorithm extends Algorithm { } const newlyOrganized: Room[] = []; - const newIndicies: ICategoryIndex = {}; + const newIndices: ICategoryIndex = {}; for (const category of CATEGORY_ORDER) { - newIndicies[category] = newlyOrganized.length; + newIndices[category] = newlyOrganized.length; newlyOrganized.push(...categorized[category]); } - this.indices[tagId] = newIndicies; + this.indices[tagId] = newIndices; updatedTagMap[tagId] = newlyOrganized; } } @@ -205,7 +202,7 @@ export class ImportanceAlgorithm extends Algorithm { } const taggedRooms = this.cached[tag]; - const indicies = this.indices[tag]; + const indices = this.indices[tag]; let roomIdx = taggedRooms.indexOf(room); if (roomIdx === -1) { console.warn(`Degrading performance to find missing room in "${tag}": ${room.roomId}`); @@ -217,16 +214,16 @@ export class ImportanceAlgorithm extends Algorithm { // Try to avoid doing array operations if we don't have to: only move rooms within // the categories if we're jumping categories - const oldCategory = this.getCategoryFromIndicies(roomIdx, indicies); + const oldCategory = this.getCategoryFromIndices(roomIdx, indices); if (oldCategory !== category) { - // Move the room and update the indicies - this.moveRoomIndexes(1, oldCategory, category, indicies); + // Move the room and update the indices + this.moveRoomIndexes(1, oldCategory, category, indices); taggedRooms.splice(roomIdx, 1); // splice out the old index (fixed position) - taggedRooms.splice(indicies[category], 0, room); // splice in the new room (pre-adjusted) + taggedRooms.splice(indices[category], 0, room); // splice in the new room (pre-adjusted) // Note: if moveRoomIndexes() is called after the splice then the insert operation // will happen in the wrong place. Because we would have already adjusted the index // for the category, we don't need to determine how the room is moving in the list. - // If we instead tried to insert before updating the indicies, we'd have to determine + // If we instead tried to insert before updating the indices, we'd have to determine // whether the room was moving later (towards IDLE) or earlier (towards RED) from its // current position, as it'll affect the category's start index after we remove the // room from the array. @@ -240,8 +237,8 @@ export class ImportanceAlgorithm extends Algorithm { // array and slot the changed room in quickly. const nextCategoryStartIdx = category === CATEGORY_ORDER[CATEGORY_ORDER.length - 1] ? Number.MAX_SAFE_INTEGER - : indicies[CATEGORY_ORDER[CATEGORY_ORDER.indexOf(category) + 1]]; - const startIdx = indicies[category]; + : indices[CATEGORY_ORDER[CATEGORY_ORDER.indexOf(category) + 1]]; + const startIdx = indices[category]; const numSort = nextCategoryStartIdx - startIdx; // splice() returns up to the max, so MAX_SAFE_INT is fine const unsortedSlice = taggedRooms.splice(startIdx, numSort); const sorted = await sortRoomsWithAlgorithm(unsortedSlice, tag, this.sortAlgorithms[tag]); @@ -253,12 +250,12 @@ export class ImportanceAlgorithm extends Algorithm { return changed; } - private getCategoryFromIndicies(index: number, indicies: ICategoryIndex): Category { + private getCategoryFromIndices(index: number, indices: ICategoryIndex): Category { for (let i = 0; i < CATEGORY_ORDER.length; i++) { const category = CATEGORY_ORDER[i]; const isLast = i === (CATEGORY_ORDER.length - 1); - const startIdx = indicies[category]; - const endIdx = isLast ? Number.MAX_SAFE_INTEGER : indicies[CATEGORY_ORDER[i + 1]]; + const startIdx = indices[category]; + const endIdx = isLast ? Number.MAX_SAFE_INTEGER : indices[CATEGORY_ORDER[i + 1]]; if (index >= startIdx && index < endIdx) { return category; } @@ -268,21 +265,21 @@ export class ImportanceAlgorithm extends Algorithm { throw new Error("Programming error: somehow you've ended up with an index that isn't in a category"); } - private moveRoomIndexes(nRooms: number, fromCategory: Category, toCategory: Category, indicies: ICategoryIndex) { + private moveRoomIndexes(nRooms: number, fromCategory: Category, toCategory: Category, indices: ICategoryIndex) { // We have to update the index of the category *after* the from/toCategory variables - // in order to update the indicies correctly. Because the room is moving from/to those + // in order to update the indices correctly. Because the room is moving from/to those // categories, the next category's index will change - not the category we're modifying. // We also need to update subsequent categories as they'll all shift by nRooms, so we // loop over the order to achieve that. for (let i = CATEGORY_ORDER.indexOf(fromCategory) + 1; i < CATEGORY_ORDER.length; i++) { const nextCategory = CATEGORY_ORDER[i]; - indicies[nextCategory] -= nRooms; + indices[nextCategory] -= nRooms; } for (let i = CATEGORY_ORDER.indexOf(toCategory) + 1; i < CATEGORY_ORDER.length; i++) { const nextCategory = CATEGORY_ORDER[i]; - indicies[nextCategory] += nRooms; + indices[nextCategory] += nRooms; } // Do a quick check to see if we've completely broken the index @@ -290,9 +287,9 @@ export class ImportanceAlgorithm extends Algorithm { const lastCat = CATEGORY_ORDER[i - 1]; const thisCat = CATEGORY_ORDER[i]; - if (indicies[lastCat] > indicies[thisCat]) { + if (indices[lastCat] > indices[thisCat]) { // "should never happen" disclaimer goes here - console.warn(`!! Room list index corruption: ${lastCat} (i:${indicies[lastCat]}) is greater than ${thisCat} (i:${indicies[thisCat]}) - category indicies are likely desynced from reality`); + console.warn(`!! Room list index corruption: ${lastCat} (i:${indices[lastCat]}) is greater than ${thisCat} (i:${indices[thisCat]}) - category indices are likely desynced from reality`); // TODO: Regenerate index when this happens } diff --git a/src/stores/room-list/models.ts b/src/stores/room-list/models.ts index 428378a7aa..a0c2621077 100644 --- a/src/stores/room-list/models.ts +++ b/src/stores/room-list/models.ts @@ -38,5 +38,5 @@ export type TagID = string | DefaultTagID; export enum RoomUpdateCause { Timeline = "TIMELINE", - RoomRead = "ROOM_READ", + RoomRead = "ROOM_READ", // TODO: Use this. } From 9fbd489b3b1fb83e7f3710ee22fd56a2c8e533e2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 13:03:43 -0600 Subject: [PATCH 165/399] Update i18n --- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d6ff5f70eb..c4912eb4d0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -406,7 +406,7 @@ "Render simple counters in room header": "Render simple counters in room header", "Multiple integration managers": "Multiple integration managers", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", - "Use the improved room list component (refresh to apply changes, in development)": "Use the improved room list component (refresh to apply changes, in development)", + "Use the improved room list (in development - refresh to apply changes)": "Use the improved room list (in development - refresh to apply changes)", "Support adding custom themes": "Support adding custom themes", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 110fd4238b..cd9ec430bf 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -133,7 +133,7 @@ export const SETTINGS = { }, "feature_new_room_list": { isFeature: true, - displayName: _td("Use the improved room list component (refresh to apply changes, in development)"), + displayName: _td("Use the improved room list (in development - refresh to apply changes)"), supportedLevels: LEVELS_FEATURE, default: false, }, From 8e047c3731a436e9cc660ad0b1078f3b971d8121 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 13:26:17 -0600 Subject: [PATCH 166/399] Update README for room list store --- src/stores/room-list/README.md | 49 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/stores/room-list/README.md b/src/stores/room-list/README.md index 0dd6c104d8..020108878b 100644 --- a/src/stores/room-list/README.md +++ b/src/stores/room-list/README.md @@ -7,20 +7,21 @@ It's so complicated it needs its own README. There's two main kinds of algorithms involved in the room list store: list ordering and tag sorting. Throughout the code an intentional decision has been made to call them the List Algorithm and Sorting Algorithm respectively. The list algorithm determines the behaviour of the room list whereas the sorting -algorithm determines how individual tags (lists of rooms, sometimes called sublists) are ordered. +algorithm determines how rooms get ordered within tags affected by the list algorithm. -Behaviour of the room list takes the shape of default sorting on tags in most cases, though it can -override what is happening at the tag level depending on the algorithm used (as is the case with the -importance algorithm, described later). +Behaviour of the room list takes the shape of determining what features the room list supports, as well +as determining where and when to apply the sorting algorithm in a tag. The importance algorithm, which +is described later in this doc, is an example of an algorithm which makes heavy behavioural changes +to the room list. Tag sorting is effectively the comparator supplied to the list algorithm. This gives the list algorithm the power to decide when and how to apply the tag sorting, if at all. ### Tag sorting algorithm: Alphabetical -When used, rooms in a given tag will be sorted alphabetically, where the alphabet is determined by a -simple string comparison operation (essentially giving the browser the problem of figuring out if A -comes before Z). +When used, rooms in a given tag will be sorted alphabetically, where the alphabet's order is a problem +for the browser. All we do is a simple string comparison and expect the browser to return something +useful. ### Tag sorting algorithm: Manual @@ -30,7 +31,7 @@ of `order` cause rooms to appear closer to the top of the list. ### Tag sorting algorithm: Recent -Rooms are ordered by the timestamp of the most recent useful message. Usefulness is yet another algorithm +Rooms get ordered by the timestamp of the most recent useful message. Usefulness is yet another algorithm in the room list system which determines whether an event type is capable of bubbling up in the room list. Normally events like room messages, stickers, and room security changes will be considered useful enough to cause a shift in time. @@ -49,7 +50,7 @@ its relative deterministic behaviour. ### List ordering algorithm: Importance On the other end of the spectrum, this is the most complicated algorithm which exists. There's major -behavioural changes and the tag sorting algorithm is selectively applied depending on circumstances. +behavioural changes, and the tag sorting algorithm gets selectively applied depending on circumstances. Each tag which is not manually ordered gets split into 4 sections or "categories". Manually ordered tags simply get the manual sorting algorithm applied to them with no further involvement from the importance @@ -58,34 +59,37 @@ relative (perceived) importance to the user: * **Red**: The room has unread mentions waiting for the user. * **Grey**: The room has unread notifications waiting for the user. Notifications are simply unread - messages which cause a push notification or badge count. Typically this is the default as rooms are + messages which cause a push notification or badge count. Typically, this is the default as rooms get set to 'All Messages'. * **Bold**: The room has unread messages waiting for the user. Essentially this is a grey room without a badge/notification count (or 'Mentions Only'/'Muted'). -* **Idle**: No relevant activity has occurred in the room since the user last read it. +* **Idle**: No useful (see definition of useful above) activity has occurred in the room since the user + last read it. -Conveniently, each tag is ordered by those categories as presented: red rooms appear above grey, grey -above idle, etc. +Conveniently, each tag gets ordered by those categories as presented: red rooms appear above grey, grey +above bold, etc. Once the algorithm has determined which rooms belong in which categories, the tag sorting algorithm -is applied to each category in a sub-sub-list fashion. This should result in the red rooms (for example) -being sorted alphabetically amongst each other and the grey rooms sorted amongst each other, but +gets applied to each category in a sub-sub-list fashion. This should result in the red rooms (for example) +being sorted alphabetically amongst each other as well as the grey rooms sorted amongst each other, but collectively the tag will be sorted into categories with red being at the top. + + The algorithm also has a concept of a 'sticky' room which is the room the user is currently viewing. The sticky room will remain in position on the room list regardless of other factors going on as typically clicking on a room will cause it to change categories into 'idle'. This is done by preserving N rooms -above the selected room at all times where N is the number of rooms above the selected rooms when it was +above the selected room at all times, where N is the number of rooms above the selected rooms when it was selected. For example, if the user has 3 red rooms and selects the middle room, they will always see exactly one -room above their selection at all times. If they receive another notification and the tag ordering is set -to Recent, they'll see the new notification go to the top position and the one that was previously there -fall behind the sticky room. +room above their selection at all times. If they receive another notification, and the tag ordering is +specified as Recent, they'll see the new notification go to the top position, and the one that was previously +there fall behind the sticky room. The sticky room's category is technically 'idle' while being viewed and is explicitly pulled out of the tag sorting algorithm's input as it must maintain its position in the list. When the user moves to another -room, the previous sticky room is recalculated to determine which category it needs to be in as the user +room, the previous sticky room gets recalculated to determine which category it needs to be in as the user could have been scrolled up while new messages were received. Further, the sticky room is not aware of category boundaries and thus the user can see a shift in what @@ -112,7 +116,10 @@ all kinds of filtering. The `RoomListStore` is the major coordinator of various `Algorithm` implementations, which take care of the various `ListAlgorithm` and `SortingAlgorithm` options. The `Algorithm` superclass is also responsible for figuring out which tags get which rooms, as Matrix specifies them as a reverse map: -tags are defined on rooms and are not defined as a collection of rooms (unlike how they are presented +tags get defined on rooms and are not defined as a collection of rooms (unlike how they are presented to the user). Various list-specific utilities are also included, though they are expected to move somewhere more general when needed. For example, the `membership` utilities could easily be moved elsewhere as needed. + +The various bits throughout the room list store should also have jsdoc of some kind to help describe +what they do and how they work. From 6cb1efc1a48b476891b488ace83108358883f0b8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 13:45:17 -0600 Subject: [PATCH 167/399] Use the new TS dispatcher --- src/actions/RoomListActions.ts | 5 +++-- src/components/views/rooms/RoomList2.tsx | 4 ++-- src/components/views/rooms/RoomTile2.tsx | 2 +- src/dispatcher-types.ts | 28 ------------------------ src/stores/AsyncStore.ts | 2 +- src/stores/room-list/RoomListStore2.ts | 3 ++- 6 files changed, 9 insertions(+), 35 deletions(-) delete mode 100644 src/dispatcher-types.ts diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index eb9831ec47..e15e1b0c65 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -16,7 +16,7 @@ limitations under the License. */ import { asyncAction } from './actionCreators'; -import RoomListStore, { TAG_DM } from '../stores/RoomListStore'; +import { TAG_DM } from '../stores/RoomListStore'; import Modal from '../Modal'; import * as Rooms from '../Rooms'; import { _t } from '../languageHandler'; @@ -24,6 +24,7 @@ import * as sdk from '../index'; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import { AsyncActionPayload } from "../dispatcher/payloads"; +import { RoomListStoreTempProxy } from "../stores/room-list/RoomListStoreTempProxy"; export default class RoomListActions { /** @@ -51,7 +52,7 @@ export default class RoomListActions { // Is the tag ordered manually? if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - const lists = RoomListStore.getRoomLists(); + const lists = RoomListStoreTempProxy.getRoomLists(); const newList = [...lists[newTag]]; newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 12a0117505..a5d0175f01 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -25,9 +25,9 @@ import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/Roo import { ITagMap } from "../../../stores/room-list/algorithms/models"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { Dispatcher } from "flux"; -import { ActionPayload } from "../../../dispatcher-types"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import RoomSublist2 from "./RoomSublist2"; +import { ActionPayload } from "../../../dispatcher/payloads"; /******************************************************************* * CAUTION * diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 8f1b0a3f7a..53e56d7a1e 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -24,7 +24,7 @@ import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomAvatar from "../../views/avatars/RoomAvatar"; import Tooltip from "../../views/elements/Tooltip"; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; import * as RoomNotifs from '../../../RoomNotifs'; import { EffectiveMembership, getEffectiveMembership } from "../../../stores/room-list/membership"; diff --git a/src/dispatcher-types.ts b/src/dispatcher-types.ts deleted file mode 100644 index 16fac0c849..0000000000 --- a/src/dispatcher-types.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2020 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 * as flux from "flux"; -import dis from "./dispatcher"; - -// TODO: Merge this with the dispatcher and centralize types - -export interface ActionPayload { - [property: string]: any; // effectively "extends Object" - action: string; -} - -// For ease of reference in TypeScript classes -export const defaultDispatcher: flux.Dispatcher = dis; diff --git a/src/stores/AsyncStore.ts b/src/stores/AsyncStore.ts index 5e19e17248..d79fd220b2 100644 --- a/src/stores/AsyncStore.ts +++ b/src/stores/AsyncStore.ts @@ -16,8 +16,8 @@ limitations under the License. import { EventEmitter } from 'events'; import AwaitLock from 'await-lock'; -import { ActionPayload } from "../dispatcher-types"; import { Dispatcher } from "flux"; +import { ActionPayload } from "../dispatcher/payloads"; /** * The event/channel to listen for in an AsyncStore. diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index c461aeab66..4f38a25d95 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -16,7 +16,6 @@ limitations under the License. */ import { MatrixClient } from "matrix-js-sdk/src/client"; -import { ActionPayload, defaultDispatcher } from "../../dispatcher-types"; import SettingsStore from "../../settings/SettingsStore"; import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models"; import { Algorithm } from "./algorithms/list_ordering/Algorithm"; @@ -25,6 +24,8 @@ import { AsyncStore } from "../AsyncStore"; import { Room } from "matrix-js-sdk/src/models/room"; import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; import { getListAlgorithmInstance } from "./algorithms/list_ordering"; +import { ActionPayload } from "../../dispatcher/payloads"; +import defaultDispatcher from "../../dispatcher/dispatcher"; interface IState { tagsEnabled?: boolean; From 91a997da1435134f1f42065b3a589a5d49b2ba07 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 14:06:48 -0600 Subject: [PATCH 168/399] Replace ChaoticAlgorithm for tag sorting with deterministic behaviour aka: implement the algorithms. --- ...ticAlgorithm.ts => AlphabeticAlgorithm.ts} | 11 ++- .../algorithms/tag_sorting/RecentAlgorithm.ts | 81 +++++++++++++++++++ .../room-list/algorithms/tag_sorting/index.ts | 7 +- 3 files changed, 92 insertions(+), 7 deletions(-) rename src/stores/room-list/algorithms/tag_sorting/{ChaoticAlgorithm.ts => AlphabeticAlgorithm.ts} (70%) create mode 100644 src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/ChaoticAlgorithm.ts b/src/stores/room-list/algorithms/tag_sorting/AlphabeticAlgorithm.ts similarity index 70% rename from src/stores/room-list/algorithms/tag_sorting/ChaoticAlgorithm.ts rename to src/stores/room-list/algorithms/tag_sorting/AlphabeticAlgorithm.ts index 31846d084a..8d74ebd11e 100644 --- a/src/stores/room-list/algorithms/tag_sorting/ChaoticAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag_sorting/AlphabeticAlgorithm.ts @@ -17,13 +17,16 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { TagID } from "../../models"; import { IAlgorithm } from "./IAlgorithm"; +import { MatrixClientPeg } from "../../../../MatrixClientPeg"; +import * as Unread from "../../../../Unread"; /** - * A demonstration to test the API surface. - * TODO: Remove this before landing + * Sorts rooms according to the browser's determination of alphabetic. */ -export class ChaoticAlgorithm implements IAlgorithm { +export class AlphabeticAlgorithm implements IAlgorithm { public async sortRooms(rooms: Room[], tagId: TagID): Promise { - return rooms; + return rooms.sort((a, b) => { + return a.name.localeCompare(b.name); + }); } } diff --git a/src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts new file mode 100644 index 0000000000..df84c051f0 --- /dev/null +++ b/src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts @@ -0,0 +1,81 @@ +/* +Copyright 2020 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 { Room } from "matrix-js-sdk/src/models/room"; +import { TagID } from "../../models"; +import { IAlgorithm } from "./IAlgorithm"; +import { MatrixClientPeg } from "../../../../MatrixClientPeg"; +import * as Unread from "../../../../Unread"; + +/** + * Sorts rooms according to the last event's timestamp in each room that seems + * useful to the user. + */ +export class RecentAlgorithm implements IAlgorithm { + public async sortRooms(rooms: Room[], tagId: TagID): Promise { + // We cache the timestamp lookup to avoid iterating forever on the timeline + // of events. This cache only survives a single sort though. + // We wouldn't need this if `.sort()` didn't constantly try and compare all + // of the rooms to each other. + + // TODO: We could probably improve the sorting algorithm here by finding changes. + // For example, if we spent a little bit of time to determine which elements have + // actually changed (probably needs to be done higher up?) then we could do an + // insertion sort or similar on the limited set of changes. + + const tsCache: { [roomId: string]: number } = {}; + const getLastTs = (r: Room) => { + if (tsCache[r.roomId]) { + return tsCache[r.roomId]; + } + + const ts = (() => { + // Apparently we can have rooms without timelines, at least under testing + // environments. Just return MAX_INT when this happens. + if (!r || !r.timeline) { + return Number.MAX_SAFE_INTEGER; + } + + for (let i = r.timeline.length - 1; i >= 0; --i) { + const ev = r.timeline[i]; + if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) + + // TODO: Don't assume we're using the same client as the peg + if (ev.getSender() === MatrixClientPeg.get().getUserId() + || Unread.eventTriggersUnreadCount(ev)) { + return ev.getTs(); + } + } + + // we might only have events that don't trigger the unread indicator, + // in which case use the oldest event even if normally it wouldn't count. + // This is better than just assuming the last event was forever ago. + if (r.timeline.length && r.timeline[0].getTs()) { + return r.timeline[0].getTs(); + } else { + return Number.MAX_SAFE_INTEGER; + } + })(); + + tsCache[r.roomId] = ts; + return ts; + }; + + return rooms.sort((a, b) => { + return getLastTs(a) - getLastTs(b); + }); + } +} diff --git a/src/stores/room-list/algorithms/tag_sorting/index.ts b/src/stores/room-list/algorithms/tag_sorting/index.ts index 155c0f0118..c22865f5ba 100644 --- a/src/stores/room-list/algorithms/tag_sorting/index.ts +++ b/src/stores/room-list/algorithms/tag_sorting/index.ts @@ -14,16 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { ChaoticAlgorithm } from "./ChaoticAlgorithm"; import { SortAlgorithm } from "../models"; import { ManualAlgorithm } from "./ManualAlgorithm"; import { IAlgorithm } from "./IAlgorithm"; import { TagID } from "../../models"; import { Room } from "matrix-js-sdk/src/models/room"; +import { RecentAlgorithm } from "./RecentAlgorithm"; +import { AlphabeticAlgorithm } from "./AlphabeticAlgorithm"; const ALGORITHM_INSTANCES: { [algorithm in SortAlgorithm]: IAlgorithm } = { - [SortAlgorithm.Recent]: new ChaoticAlgorithm(), - [SortAlgorithm.Alphabetic]: new ChaoticAlgorithm(), + [SortAlgorithm.Recent]: new RecentAlgorithm(), + [SortAlgorithm.Alphabetic]: new AlphabeticAlgorithm(), [SortAlgorithm.Manual]: new ManualAlgorithm(), }; From b7ba9b3c413043ac759aacb15fe6650f2be2a73b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 14:16:26 -0600 Subject: [PATCH 169/399] Replace ChaoticAlgorithm with NaturalAlgorithm for list behaviour --- .../list_ordering/ChaoticAlgorithm.ts | 38 ------------- .../list_ordering/NaturalAlgorithm.ts | 56 +++++++++++++++++++ .../algorithms/list_ordering/index.ts | 4 +- 3 files changed, 58 insertions(+), 40 deletions(-) delete mode 100644 src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts create mode 100644 src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts diff --git a/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts deleted file mode 100644 index 185fb606fb..0000000000 --- a/src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2020 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 { Algorithm } from "./Algorithm"; -import { ITagMap } from "../models"; - -/** - * A demonstration/temporary algorithm to verify the API surface works. - * TODO: Remove this before shipping - */ -export class ChaoticAlgorithm extends Algorithm { - - constructor() { - super(); - console.log("Constructed a ChaoticAlgorithm"); - } - - protected async generateFreshTags(updatedTagMap: ITagMap): Promise { - return Promise.resolve(); - } - - public async handleRoomUpdate(room, cause): Promise { - return Promise.resolve(false); - } -} diff --git a/src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts new file mode 100644 index 0000000000..1265564352 --- /dev/null +++ b/src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts @@ -0,0 +1,56 @@ +/* +Copyright 2020 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 { Algorithm } from "./Algorithm"; +import { ITagMap } from "../models"; +import { sortRoomsWithAlgorithm } from "../tag_sorting"; + +/** + * Uses the natural tag sorting algorithm order to determine tag ordering. No + * additional behavioural changes are present. + */ +export class NaturalAlgorithm extends Algorithm { + + constructor() { + super(); + console.log("Constructed a NaturalAlgorithm"); + } + + protected async generateFreshTags(updatedTagMap: ITagMap): Promise { + for (const tagId of Object.keys(updatedTagMap)) { + const unorderedRooms = updatedTagMap[tagId]; + + const sortBy = this.sortAlgorithms[tagId]; + if (!sortBy) throw new Error(`${tagId} does not have a sorting algorithm`); + + updatedTagMap[tagId] = await sortRoomsWithAlgorithm(unorderedRooms, tagId, sortBy); + } + } + + public async handleRoomUpdate(room, cause): Promise { + const tags = this.roomIdsToTags[room.roomId]; + if (!tags) { + console.warn(`No tags known for "${room.name}" (${room.roomId})`); + return false; + } + for (const tag of tags) { + // TODO: Optimize this loop to avoid useless operations + // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags + this.cached[tag] = await sortRoomsWithAlgorithm(this.cached[tag], tag, this.sortAlgorithms[tag]); + } + return true; // assume we changed something + } +} diff --git a/src/stores/room-list/algorithms/list_ordering/index.ts b/src/stores/room-list/algorithms/list_ordering/index.ts index 35f4af14cf..bcccd150cd 100644 --- a/src/stores/room-list/algorithms/list_ordering/index.ts +++ b/src/stores/room-list/algorithms/list_ordering/index.ts @@ -15,12 +15,12 @@ limitations under the License. */ import { Algorithm } from "./Algorithm"; -import { ChaoticAlgorithm } from "./ChaoticAlgorithm"; import { ImportanceAlgorithm } from "./ImportanceAlgorithm"; import { ListAlgorithm } from "../models"; +import { NaturalAlgorithm } from "./NaturalAlgorithm"; const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => Algorithm } = { - [ListAlgorithm.Natural]: () => new ChaoticAlgorithm(), + [ListAlgorithm.Natural]: () => new NaturalAlgorithm(), [ListAlgorithm.Importance]: () => new ImportanceAlgorithm(), }; From 5cfe29de668161ab4ed82e05e4890deee94b5893 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 14 May 2020 14:20:01 -0600 Subject: [PATCH 170/399] Update AsyncStore's docs to be slightly more clear --- src/stores/AsyncStore.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/stores/AsyncStore.ts b/src/stores/AsyncStore.ts index d79fd220b2..3519050078 100644 --- a/src/stores/AsyncStore.ts +++ b/src/stores/AsyncStore.ts @@ -29,9 +29,11 @@ export const UPDATE_EVENT = "update"; * of everything needing to happen in a dispatch cycle, everything can * happen async to that cycle. * - * The store's core principle is Object.assign(), therefore it is recommended - * to break out your state to be as safe as possible. The state mutations are - * also locked, preventing concurrent writes. + * The store operates by using Object.assign() to mutate state - it sends the + * state objects (current and new) through the function onto a new empty + * object. Because of this, it is recommended to break out your state to be as + * safe as possible. The state mutations are also locked, preventing concurrent + * writes. * * All updates to the store happen on the UPDATE_EVENT event channel with the * one argument being the instance of the store. From 21e471375ee2cb52d950c27c337530f389dbe0d7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 12 Mar 2020 13:34:56 -0600 Subject: [PATCH 171/399] Revert "Add temporary timing functions to old RoomListStore" This reverts commit 82b55ffd7717b4256690f43eb6891b173fe3cec9. --- src/stores/RoomListStore.js | 60 +++++-------------------------------- 1 file changed, 8 insertions(+), 52 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index b0690e7e69..c19b2f8bc2 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -58,27 +58,7 @@ export const ALGO_RECENT = "recent"; const CATEGORY_ORDER = [CATEGORY_RED, CATEGORY_GREY, CATEGORY_BOLD, CATEGORY_IDLE]; -function debugLog(...msg) { - console.log(`[RoomListStore:Debug] `, ...msg); -} - -const timers = {}; -let timerCounter = 0; -function startTimer(fnName) { - const id = `${fnName}_${(new Date()).getTime()}_${timerCounter++}`; - debugLog(`Started timer for ${fnName} with ID ${id}`); - timers[id] = {start: (new Date()).getTime(), fnName}; - return id; -} - -function endTimer(id) { - const timer = timers[id]; - delete timers[id]; - const diff = (new Date()).getTime() - timer.start; - debugLog(`${timer.fnName} took ${diff}ms (ID: ${id})`); -} - -function getListAlgorithm(listKey, settingAlgorithm) { +const getListAlgorithm = (listKey, settingAlgorithm) => { // apply manual sorting only to m.favourite, otherwise respect the global setting // all the known tags are listed explicitly here to simplify future changes switch (listKey) { @@ -93,7 +73,7 @@ function getListAlgorithm(listKey, settingAlgorithm) { default: // custom-tags return ALGO_MANUAL; } -} +}; const knownLists = new Set([ "m.favourite", @@ -377,7 +357,6 @@ class RoomListStore extends Store { } _getRecommendedTagsForRoom(room) { - const timerId = startTimer(`_getRecommendedTagsForRoom(room:"${room.roomId}")`); const tags = []; const myMembership = room.getMyMembership(); @@ -403,12 +382,11 @@ class RoomListStore extends Store { tags.push("im.vector.fake.archived"); } - endTimer(timerId); + return tags; } _slotRoomIntoList(room, category, tag, existingEntries, newList, lastTimestampFn) { - const timerId = startTimer(`_slotRoomIntoList(room:"${room.roomId}", "${category}", "${tag}", existingEntries: "${existingEntries.length}", "${newList}", lastTimestampFn:"${lastTimestampFn !== null}")`); const targetCategoryIndex = CATEGORY_ORDER.indexOf(category); let categoryComparator = (a, b) => lastTimestampFn(a.room) >= lastTimestampFn(b.room); @@ -520,16 +498,11 @@ class RoomListStore extends Store { pushedEntry = true; } - endTimer(timerId); return pushedEntry; } _setRoomCategory(room, category) { - const timerId = startTimer(`_setRoomCategory(room:"${room.roomId}", "${category}")`); - if (!room) { - endTimer(timerId); - return; // This should only happen in tests - } + if (!room) return; // This should only happen in tests const listsClone = {}; @@ -626,11 +599,9 @@ class RoomListStore extends Store { } this._setState({lists: listsClone}); - endTimer(timerId); } _generateInitialRoomLists() { - const timerId = startTimer(`_generateInitialRoomLists()`); // Log something to show that we're throwing away the old results. This is for the inevitable // question of "why is 100% of my CPU going towards Riot?" - a quick look at the logs would reveal // that something is wrong with the RoomListStore. @@ -743,7 +714,6 @@ class RoomListStore extends Store { lists, ready: true, // Ready to receive updates to ordering }); - endTimer(timerId); } _eventTriggersRecentReorder(ev) { @@ -756,9 +726,7 @@ class RoomListStore extends Store { _tsOfNewestEvent(room) { // Apparently we can have rooms without timelines, at least under testing // environments. Just return MAX_INT when this happens. - if (!room || !room.timeline) { - return Number.MAX_SAFE_INTEGER; - } + if (!room || !room.timeline) return Number.MAX_SAFE_INTEGER; for (let i = room.timeline.length - 1; i >= 0; --i) { const ev = room.timeline[i]; @@ -778,35 +746,23 @@ class RoomListStore extends Store { } _calculateCategory(room) { - const timerId = startTimer(`_calculateCategory(room:"${room.roomId}")`); if (!this._state.orderImportantFirst) { // Effectively disable the categorization of rooms if we're supposed to // be sorting by more recent messages first. This triggers the timestamp // comparison bit of _setRoomCategory and _recentsComparator instead of // the category ordering. - endTimer(timerId); return CATEGORY_IDLE; } const mentions = room.getUnreadNotificationCount("highlight") > 0; - if (mentions) { - endTimer(timerId); - return CATEGORY_RED; - } + if (mentions) return CATEGORY_RED; let unread = room.getUnreadNotificationCount() > 0; - if (unread) { - endTimer(timerId); - return CATEGORY_GREY; - } + if (unread) return CATEGORY_GREY; unread = Unread.doesRoomHaveUnreadMessages(room); - if (unread) { - endTimer(timerId); - return CATEGORY_BOLD; - } + if (unread) return CATEGORY_BOLD; - endTimer(timerId); return CATEGORY_IDLE; } From 59f1657b4382a88ad87396af8852a6f9d3a0682a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 15 May 2020 00:25:19 +0100 Subject: [PATCH 172/399] Add jest-canvas-mock Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index dda4a5a897..708ae91b0b 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "flow-parser": "^0.57.3", "glob": "^5.0.14", "jest": "^24.9.0", + "jest-canvas-mock": "^2.2.0", "lolex": "^5.1.2", "matrix-mock-request": "^1.2.3", "matrix-react-test-utils": "^0.2.2", @@ -157,6 +158,7 @@ "testMatch": [ "/test/**/*-test.js" ], + "setupFiles": ["jest-canvas-mock"], "setupFilesAfterEnv": [ "/test/setupTests.js" ], From e79959a4eca99a7a4155851ec0860a01a55f371d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 15 May 2020 00:31:30 +0100 Subject: [PATCH 173/399] try fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/messages/TextualBody-test.js | 4 +-- yarn.lock | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 59671327ce..364d239038 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -205,9 +205,9 @@ describe("", () => { expect(content.html()).toBe('' + 'Hey ' + '' + - 'Member' + + 'title="@member:domain.bla" alt="" aria-hidden="true" src="mxc://avatar.url/image.png">Member' + ''); }); }); diff --git a/yarn.lock b/yarn.lock index b0d3816dc4..0e9fe51490 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2470,6 +2470,11 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@~0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" + integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0= + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -2723,6 +2728,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssfontparser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3" + integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M= + cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" @@ -4947,6 +4957,14 @@ istanbul-reports@^2.2.6: dependencies: html-escaper "^2.0.0" +jest-canvas-mock@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.2.0.tgz#45fbc58589c6ce9df50dc90bd8adce747cbdada7" + integrity sha512-DcJdchb7eWFZkt6pvyceWWnu3lsp5QWbUeXiKgEMhwB3sMm5qHM1GQhDajvJgBeiYpgKcojbzZ53d/nz6tXvJw== + dependencies: + cssfontparser "^1.2.1" + parse-color "^1.0.0" + jest-changed-files@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" @@ -6404,6 +6422,13 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-color@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619" + integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk= + dependencies: + color-convert "~0.5.0" + parse-entities@^1.0.2, parse-entities@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" From b6ac8cb28d92822ea1cf128fd46b687113e1a74d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Wed, 13 May 2020 06:36:14 +0100 Subject: [PATCH 174/399] Revert "ImageView make clicking off it easier" (cherry picked from commit 52e3c97f8c9a2032fe92b2b4bb5fc68c0f6957b5) --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 983ef074f2..0a4ed2a194 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -37,7 +37,7 @@ limitations under the License. order: 2; /* min-width hack needed for FF */ min-width: 0px; - max-height: 90%; + height: 90%; flex: 15 15 0; display: flex; align-items: center; From a864643d98197ae6bc3bf34c8ee3da4c4a94a013 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 18 May 2020 10:37:02 +0100 Subject: [PATCH 175/399] Label the create room button something better than "Add room" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomList.js | 1 + src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 289a89a206..c46b946b5c 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -785,6 +785,7 @@ export default createReactClass({ label: _t('Rooms'), incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'), onAddRoom: () => {dis.dispatch({action: 'view_create_room'});}, + addRoomLabel: _t("Create room"), }, ]; const tagSubLists = Object.keys(this.state.lists) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f16a0d7755..c9ac768b3e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1113,6 +1113,7 @@ "Direct Messages": "Direct Messages", "Start chat": "Start chat", "Rooms": "Rooms", + "Create room": "Create room", "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", From a8e92589ad9c88db00a69a7debfd8d601378c3cf Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Mon, 18 May 2020 09:17:10 +0000 Subject: [PATCH 176/399] Translated using Weblate (Slovak) Currently translated at 62.7% (1450 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index e85732ed82..168f2b4f39 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -1018,7 +1018,7 @@ "Failed to decrypt %(failedCount)s sessions!": "Nepodarilo sa dešifrovať %(failedCount)s relácií!", "Restored %(sessionCount)s session keys": "Obnovených %(sessionCount)s kľúčov relácií", "Enter Recovery Passphrase": "Zadajte heslo bezpečného obnovenia", - "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Zabezpečte svoju komunikáciu a prístup k šifrovanej histórii konverzácií zadaním hesla obnovenia.", + "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Zabezpečte svoju komunikáciu a prístup k šifrovanej histórii konverzácií zadaním (dlhého) hesla obnovenia.", "Waiting for %(userId)s to confirm...": "Čakanie na potvrdenie od používateľa %(userId)s…", "Prompt before sending invites to potentially invalid matrix IDs": "Upozorniť pred odoslaním pozvaní na potenciálne neexistujúce Matrix ID", "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Nie je možné nájsť používateľský profil pre Matrix ID zobrazené nižšie. Chcete ich napriek tomu pozvať?", @@ -1531,5 +1531,26 @@ "Keep recovery passphrase in memory for this session": "Ponechať (dlhé) heslo pre obnovu zálohy v pamäti pre túto reláciu", "Enter recovery passphrase": "Zadajte (dlhé) heslo pre obnovu zálohy", "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Nemožno sa dostať do tajného úložiska. Prosím, overte, že ste zadali správne (dlhé) heslo pre obnovu zálohy.", - "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej zabezpečenej histórií správ a vašemu krížom-podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy." + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej šifrovanej histórií správ a vašemu krížom-podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy.", + "Encryption upgrade available": "Je dostupná aktualizácia šifrovania", + "Set up encryption": "Nastaviť šifrovanie", + "Review where you’re logged in": "Zobraziť, kde ste prihlásený", + "New login. Was this you?": "Nové pihlásenie. Ste to vy?", + "%(name)s is requesting verification": "%(name) žiada o overenie", + "Sign In or Create Account": "Prihlásiť sa alebo vytvoriť nový účet", + "Use your account or create a new one to continue.": "Použite váš existujúci účet alebo vytvorte si nový, aby ste mohli pokračovať.", + "Create Account": "Vytvoriť účet", + "Sign In": "Prihlásiť sa", + "Sends a message as html, without interpreting it as markdown": "Pošlite správu ako HTML, bez interpretácie v Markdowne", + "Failed to set topic": "Nastavenie témy zlyhalo", + "Command failed": "Príkaz zlyhal", + "Could not find user in room": "Nepodarilo sa nájsť používateľa v miestnosti", + "Please supply a widget URL or embed code": "Prosím, zadajte URL widgetu alebo vložte kód", + "Verifies a user, session, and pubkey tuple": "Overí používateľa, reláciu a verejné kľúče", + "Unknown (user, session) pair:": "Neznámy pár (používateľ, relácia):", + "Session already verified!": "Relácia je už overená!", + "WARNING: Session already verified, but keys do NOT MATCH!": "VAROVANIE: Relácia je už overená, ale kľúče sa NEZHODUJÚ!", + "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Pokiaľ ste zabudli na vaše (dlhé) heslo na obnovu zálohy, môžete použiť váš kľúč na obnovu zálohy alebo nastaviť nové spôsoby obnovy zálohy.", + "Incorrect recovery passphrase": "Nesprávne (dlhé) heslo pre obnovu zálohy", + "Backup could not be decrypted with this recovery passphrase: please verify that you entered the correct recovery passphrase.": "Záloha nemohla byť rozšifrovaná pomocou tohto (dlhého) helsa na obnovu zálohy: prosím, overte, či ste zadali správne (dlhé) helso na obnovu zálohy." } From 17f535e5f878337854b7599ca8da2b3e8528c81b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 18 May 2020 16:37:10 +0100 Subject: [PATCH 177/399] Fix review problems - removed superfluous position and classes - fixed compact view - fixed event list summary avatar and text overlap - fixed a problem where the mention list refuses to load. --- res/css/views/rooms/_EventTile.scss | 79 ------------------ res/css/views/rooms/_GroupLayout.scss | 82 ++++++++++++++++++- res/css/views/rooms/_IRCLayout.scss | 4 +- src/components/structures/MessagePanel.js | 2 +- .../views/elements/ErrorBoundary.js | 2 +- .../elements/IRCTimelineProfileResizer.tsx | 4 +- src/components/views/rooms/EventTile.js | 2 - 7 files changed, 88 insertions(+), 87 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index b9a41c4310..c278f813d0 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -37,7 +37,6 @@ limitations under the License. } .mx_EventTile_avatar { - position: absolute; top: 14px; left: 8px; cursor: pointer; @@ -576,84 +575,6 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody { /* end of overrides */ -.mx_MatrixChat_useCompactLayout { - .mx_EventTile { - padding-top: 4px; - } - - .mx_EventTile.mx_EventTile_info { - // same as the padding for non-compact .mx_EventTile.mx_EventTile_info - padding-top: 0px; - font-size: $font-13px; - .mx_EventTile_line, .mx_EventTile_reply { - line-height: $font-20px; - } - .mx_EventTile_avatar { - top: 4px; - } - } - - .mx_EventTile .mx_SenderProfile { - font-size: $font-13px; - } - - .mx_EventTile.mx_EventTile_emote { - // add a bit more space for emotes so that avatars don't collide - padding-top: 8px; - .mx_EventTile_avatar { - top: 2px; - } - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 1px; - } - } - - .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { - padding-top: 0; - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - } - - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - - .mx_EventTile_avatar { - top: 2px; - } - - .mx_EventTile_e2eIcon { - top: 3px; - } - - .mx_EventTile_readAvatars { - top: 27px; - } - - .mx_EventTile_continuation .mx_EventTile_readAvatars, - .mx_EventTile_emote .mx_EventTile_readAvatars { - top: 5px; - } - - .mx_EventTile_info .mx_EventTile_readAvatars { - top: 4px; - } - - .mx_RoomView_MessageList h2 { - margin-top: 6px; - } - - .mx_EventTile_content .markdown-body { - p, ul, ol, dl, blockquote, pre, table { - margin-bottom: 4px; // 1/4 of the non-compact margin-bottom - } - } -} - .mx_EventTile_tileError { color: red; text-align: center; diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 6528d6c6cd..bfe463ed49 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -47,6 +47,86 @@ $left-gutter: 65px; } .mx_EventTile_info .mx_EventTile_line { - padding-left: 83px; + padding-left: calc($left-gutter + 18px); } } + +/* Compact layout overrides */ + +.mx_MatrixChat_useCompactLayout { + .mx_EventTile { + padding-top: 4px; + } + + .mx_EventTile.mx_EventTile_info { + // same as the padding for non-compact .mx_EventTile.mx_EventTile_info + padding-top: 0px; + font-size: $font-13px; + .mx_EventTile_line, .mx_EventTile_reply { + line-height: $font-20px; + } + .mx_EventTile_avatar { + top: 4px; + } + } + + .mx_EventTile .mx_SenderProfile { + font-size: $font-13px; + } + + .mx_EventTile.mx_EventTile_emote { + // add a bit more space for emotes so that avatars don't collide + padding-top: 8px; + .mx_EventTile_avatar { + top: 2px; + } + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 1px; + } + } + + .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { + padding-top: 0; + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + + .mx_EventTile_avatar { + top: 2px; + } + + .mx_EventTile_e2eIcon { + top: 3px; + } + + .mx_EventTile_readAvatars { + top: 27px; + } + + .mx_EventTile_continuation .mx_EventTile_readAvatars, + .mx_EventTile_emote .mx_EventTile_readAvatars { + top: 5px; + } + + .mx_EventTile_info .mx_EventTile_readAvatars { + top: 4px; + } + + .mx_RoomView_MessageList h2 { + margin-top: 6px; + } + + .mx_EventTile_content .markdown-body { + p, ul, ol, dl, blockquote, pre, table { + margin-bottom: 4px; // 1/4 of the non-compact margin-bottom + } + } +} \ No newline at end of file diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index f2a616f9c9..5f88473c5f 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -138,7 +138,7 @@ $irc-line-height: $font-18px; .mx_EventListSummary_avatars { padding: 0; - margin: 0; + margin: 0 9px 0 0; } } @@ -185,7 +185,7 @@ $irc-line-height: $font-18px; .mx_SenderProfile_hover:hover { overflow: visible; - width: auto; + width: max(auto, 100%); z-index: 10; } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 1c10efb346..f875467e4f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -822,7 +822,7 @@ export default class MessagePanel extends React.Component { let ircResizer = null; if (this.state.useIRCLayout) { - ircResizer = ; + ircResizer = ; } return ( diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index 1abd11f838..a043b350ab 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -73,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; - return
    + return

    {_t("Something went wrong!")}

    {_t( diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 44ceeb9b7b..596d46bf36 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -77,7 +77,9 @@ export default class IRCTimelineProfileResizer extends React.Component Date: Mon, 18 May 2020 16:43:47 +0100 Subject: [PATCH 178/399] lint --- res/css/views/rooms/_GroupLayout.scss | 2 +- src/components/structures/MessagePanel.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index bfe463ed49..40440f7d49 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -129,4 +129,4 @@ $left-gutter: 65px; margin-bottom: 4px; // 1/4 of the non-compact margin-bottom } } -} \ No newline at end of file +} diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index f875467e4f..b3f9b40ada 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -822,7 +822,11 @@ export default class MessagePanel extends React.Component { let ircResizer = null; if (this.state.useIRCLayout) { - ircResizer = ; + ircResizer = ; } return ( From 4deeef5fca9afcaf35d3c76f20ea84c8ac4808aa Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 18 May 2020 16:57:00 +0100 Subject: [PATCH 179/399] Remove ability to remove avatars --- src/components/structures/MessagePanel.js | 13 ------------- src/components/views/elements/ReplyThread.js | 7 ++----- src/components/views/rooms/EventTile.js | 6 +----- src/settings/Settings.js | 6 ------ 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b3f9b40ada..404e950d7f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -174,11 +174,6 @@ export default class MessagePanel extends React.Component { SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); - this._displayAvatarsWatcherRef = SettingsStore.watchSetting( - "feature_no_timeline_avatars", - null, - this.onDisplayAvatarsChange, - ); } componentDidMount() { @@ -189,7 +184,6 @@ export default class MessagePanel extends React.Component { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); SettingsStore.unwatchSetting(this._layoutWatcherRef); - SettingsStore.unwatchSetting(this._displayAvatarsWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -214,12 +208,6 @@ export default class MessagePanel extends React.Component { }); } - onDisplayAvatarsChange = () => { - this.setState({ - displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), - }); - } - /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -622,7 +610,6 @@ export default class MessagePanel extends React.Component { getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} useIRCLayout={this.state.useIRCLayout} - displayAvatars={this.state.displayAvatars} /> , diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 6bfda5dd94..04e31d6490 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -39,8 +39,6 @@ export default class ReplyThread extends React.Component { permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, // Specifies which layout to use. useIRCLayout: PropTypes.bool, - // Specifies whether to display avatars. - displayAvatars: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -180,7 +178,7 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout, displayAvatars) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { return

    ; } @@ -190,7 +188,7 @@ export default class ReplyThread extends React.Component { ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} - displayAvatars={displayAvatars} />; + />; } componentDidMount() { @@ -342,7 +340,6 @@ export default class ReplyThread extends React.Component { isRedacted={ev.isRedacted()} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} useIRCLayout={this.props.useIRCLayout} - displayAvatars={this.props.displayAvatars} /> ; }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 4ff3a5ccfc..1cb632b2e8 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -209,9 +209,6 @@ export default createReactClass({ // whether to use the irc layout useIRCLayout: PropTypes.bool, - - // whether to display avatars - displayAvatars: PropTypes.bool, }, getDefaultProps: function() { @@ -714,7 +711,7 @@ export default createReactClass({ needsSenderProfile = true; } - if (this.props.mxEvent.sender && avatarSize && this.props.displayAvatars) { + if (this.props.mxEvent.sender && avatarSize) { avatar = (
    Date: Mon, 18 May 2020 17:06:54 +0100 Subject: [PATCH 180/399] Appease i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 37b9c1dfc8..12838968f7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -406,7 +406,6 @@ "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Support adding custom themes": "Support adding custom themes", "Use IRC layout": "Use IRC layout", - "Display user avatars on messages": "Display user avatars on messages", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", From 7bb7f30b8f4ca9c8bd3d02e69ff2928317626fa9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 18 May 2020 22:02:22 +0100 Subject: [PATCH 181/399] missed one --- src/components/structures/MessagePanel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 404e950d7f..cac04d84f1 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -119,7 +119,6 @@ export default class MessagePanel extends React.Component { ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), useIRCLayout: SettingsStore.getValue("feature_irc_ui"), - displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker From b9c31b27a2526c5670b43e3506008a0366e4a639 Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Mon, 18 May 2020 10:14:36 +0000 Subject: [PATCH 182/399] Translated using Weblate (Slovak) Currently translated at 64.9% (1500 of 2311 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 93 +++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 168f2b4f39..d117843ccf 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -814,7 +814,7 @@ "Invite to this room": "Pozvať do tejto miestnosti", "You cannot delete this message. (%(code)s)": "Nemôžete vymazať túto správu. (%(code)s)", "Thursday": "Štvrtok", - "I understand the risks and wish to continue": "Rozumiem rizikám a želám si pokračovať", + "I understand the risks and wish to continue": "Rozumiem riziku a chcem pokračovať", "Logs sent": "Záznamy boli odoslané", "Back": "Naspäť", "Reply": "Odpovedať", @@ -1018,7 +1018,7 @@ "Failed to decrypt %(failedCount)s sessions!": "Nepodarilo sa dešifrovať %(failedCount)s relácií!", "Restored %(sessionCount)s session keys": "Obnovených %(sessionCount)s kľúčov relácií", "Enter Recovery Passphrase": "Zadajte heslo bezpečného obnovenia", - "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Zabezpečte svoju komunikáciu a prístup k šifrovanej histórii konverzácií zadaním (dlhého) hesla obnovenia.", + "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Získajte prístup k šifrovanej histórií správ a nastavte šiforvanú komunikáciu zadaním vášho (dlhého) hesla obnovenia.", "Waiting for %(userId)s to confirm...": "Čakanie na potvrdenie od používateľa %(userId)s…", "Prompt before sending invites to potentially invalid matrix IDs": "Upozorniť pred odoslaním pozvaní na potenciálne neexistujúce Matrix ID", "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Nie je možné nájsť používateľský profil pre Matrix ID zobrazené nižšie. Chcete ich napriek tomu pozvať?", @@ -1029,7 +1029,7 @@ "Enter Recovery Key": "Zadajte kľúč obnovenia", "This looks like a valid recovery key!": "Zdá sa, že toto je platný kľúč obnovenia!", "Not a valid recovery key": "Neplatný kľúč obnovenia", - "Access your secure message history and set up secure messaging by entering your recovery key.": "Zabezpečte svoju komunikáciu a prístup k šifrovanej histórii konverzácií zadaním kľúča obnovenia.", + "Access your secure message history and set up secure messaging by entering your recovery key.": "Získajte prístup k šifrovanej histórií správ a nastavte šiforvanú komunikáciu zadaním vášho kľúča obnovenia.", "Set a new status...": "Nastaviť nový stav…", "Clear status": "Zrušiť stav", "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Ste správcom tejto komunity. Nebudete môcť znovu vstúpiť bez pozvania od iného správcu.", @@ -1041,8 +1041,8 @@ "Great! This passphrase looks strong enough.": "Výborne! Toto je dostatočne silné heslo.", "Enter a passphrase...": "Zadajte heslo…", "That matches!": "Zhoda!", - "That doesn't match.": "Nezhodujú sa.", - "Go back to set it again.": "Vráťte sa späť a nastavte znovu.", + "That doesn't match.": "To sa nezhoduje.", + "Go back to set it again.": "Vráťte sa späť a nastavte to znovu.", "Repeat your passphrase...": "Zopakujte heslo…", "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "Ak zabudnete svoje heslo obnovenia, tento kľúč môžete použiť ako ďalší bezpečnostný prvok na obnovenie histórii šifrovaných konverzácií.", "As a safety net, you can use it to restore your encrypted message history.": "Tento kľúč môžete použiť ako ďalší bezpečnostný prvok na obnovenie histórii šifrovaných konverzácií.", @@ -1059,8 +1059,8 @@ "Retry": "Skúsiť znovu", "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Ak si nenastavíte Bezpečné obnovenie správ, po odhlásení stratíte prístup k histórii šifrovaných konverzácií.", "If you don't want to set this up now, you can later in Settings.": "Ak nechcete pokračovať v nastavení teraz, môžete sa k tomu vrátiť neskôr v časti nastavenia.", - "New Recovery Method": "Nový spôsob obnovi", - "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Ak ste si nenastavili nový spôsob obnovenia útočníci sa môžu pokúsiť dostať k vášmu účtu. Ihneď si v nastaveniach zmeňte heslo a znovu si nastavte možnosti obnovenia.", + "New Recovery Method": "Nový spôsob obnovy", + "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Ak ste si nenastavili nový spôsob obnovenia, je možné, že útočník sa pokúša dostať k vášmu účtu. Radšej si ihneď zmeňte vaše heslo a nastavte si nový spôsob obnovenia v Nastaveniach.", "Set up Secure Messages": "Nastaviť bezpečné obnovenie správ", "Go to Settings": "Otvoriť nastavenia", "Whether or not you're logged in (we don't record your username)": "Či ste alebo nie ste prihlásení (nezaznamenávame vaše meno používateľa)", @@ -1321,7 +1321,7 @@ "Premium hosting for organisations Learn more": "Platený hosting pre organizácie Zistiť viac", "Other": "Ďalšie", "Find other public servers or use a custom server": "Nájdite ďalšie verejné domovské servery alebo nastavte pripojenie k serveru ručne", - "Please install Chrome, Firefox, or Safari for the best experience.": "Pre najlepší zážitok si prosím nainštalujte Chrome, Firefox alebo Safari.", + "Please install Chrome, Firefox, or Safari for the best experience.": "Prosím, nainštalujte si Chrome, Firefox alebo Safari pre najlepší zážitok.", "Couldn't load page": "Nie je možné načítať stránku", "Want more than a community? Get your own server": "Chceli by ste viac než komunitu? Získajte vlastný server", "This homeserver does not support communities": "Tento domovský server nepodporuje komunity", @@ -1339,7 +1339,7 @@ "Create your account": "Vytvoriť váš účet", "Keep going...": "Pokračujte…", "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Zašifrovanú kópiu vašich šifrovacích kľúčov uchováme na domovskom servery. Zabezpečte si zálohovanie zadaním hesla obnovenia, čo posilní ochranu vašich údajov.", - "For maximum security, this should be different from your account password.": "Aby ste zachovali maximálnu mieru zabezpečenia, heslo obnovenia by malo byť odlišné ako heslo, ktorým sa prihlasujete do Matrix účtu.", + "For maximum security, this should be different from your account password.": "Aby ste zachovali maximálnu mieru zabezpečenia, (dlhé) heslo by malo byť odlišné od hesla, ktorým sa prihlasujete do vášho účtu.", "Set up with a Recovery Key": "Nastaviť použitím kľúča obnovenia", "Please enter your passphrase a second time to confirm.": "Prosím zadajte heslo obnovenia ešte raz pre potvrdenie.", "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kľúč obnovenia je bezpečnostný mechanizmus - môžete ho použiť na prístup k šifrovacím kľúčom v prípade, ak zabudnete vaše heslo obnovenia.", @@ -1348,10 +1348,10 @@ "Confirm your passphrase": "Potvrdiť heslo obnovenia", "Recovery key": "Kľúč obnovenia", "Starting backup...": "Začína sa zálohovanie…", - "Success!": "Hotovo!", - "A new recovery passphrase and key for Secure Messages have been detected.": "Boli zistené nový kľúč a nové heslo obnovenia zálohovania šifrovacích kľúčov.", + "Success!": "Úspech!", + "A new recovery passphrase and key for Secure Messages have been detected.": "Nové (dlhé) heslo na obnovu zálohy a kľúč pre bezpečné správy boli spozorované.", "Recovery Method Removed": "Odstránený spôsob obnovenia", - "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.": "Ak ste spôsob obnovenia neodstránili vy, útočník sa pravdepodobne usiluje dostať k vašemu účtu. Zmente si prosím heslo na prihlásenie do Matrix účtu a znovu si ihneď nastavte možnosti obnovenia.", + "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.": "Ak ste neodstránili spôsob obnovenia vy, je možné, že útočník sa pokúša dostať k vášmu účtu. Radšej si ihneď zmeňte vaše heslo a nastavte si nový spôsob obnovenia v Nastaveniach.", "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Či používate alebo nie funkcionalitu známu ako „omrvinky“ (obrázky nad zoznamom miestností)", "Call failed due to misconfigured server": "Hovor zlyhal kvôli nesprávne nakonfigurovanému serveru", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Prosím, požiadajte správcu vášho domovského servera (%(homeserverDomain)s) aby nakonfiguroval Turn server, čo zlepší spoľahlivosť audio / video hovorov.", @@ -1410,7 +1410,7 @@ "Send cross-signing keys to homeserver": "Poslať kľúče pre podpisovanie naprieč zariadeniami na domovský server", "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Táto akcia si vyžaduje mať overenú emailovú adresu alebo telefónne číslo cez predvolený server totožností , ale server nezverejnil podmienky používania.", "Trust": "Dôverovať", - "Custom (%(level)s)": "Vlastná (%(level)s)", + "Custom (%(level)s)": "Vlastný (%(level)s)", "Sends a message as plain text, without interpreting it as markdown": "Odošle správu vo formáte obyčajný text, bez prekladu markdown", "You do not have the required permissions to use this command.": "Na použitie tohoto príkazu nemáte dostatočné povolenia.", "Error upgrading room": "Chyba pri aktualizácii miestnosti", @@ -1441,7 +1441,7 @@ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť z domovských serverov pôvodne sa zhodujúcich s %(oldGlob)s na servery zhodujúce sa s %(newGlob)s, dôvod: %(reason)s", "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť pôvodne sa zhodujúce s %(oldGlob)s na %(newGlob)s, dôvod: %(reason)s", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", - "Multiple integration managers": "Viacej integračných serverov", + "Multiple integration managers": "Viac integračných serverov", "Try out new ways to ignore people (experimental)": "Vyskúšajte si nový spôsob ignorovania používateľov (experiment)", "Enable local event indexing and E2EE search (requires restart)": "Povoliť lokálne indexovanie udalostí a vyhľadávanie v šifrovaných miestnostiach", "Match system theme": "Prispôsobiť sa vzhľadu systému", @@ -1452,12 +1452,12 @@ "The message you are trying to send is too large.": "Správa, ktorú sa usilujete odoslať, je príliš veľká.", "This is your list of users/servers you have blocked - don't leave the room!": "Toto je zoznam používateľov / serverov, ktorých ste zablokovali - neopúšťajte miestnosť!", "Upload": "Nahrať", - "Cross-signing and secret storage are enabled.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko sú aktívne.", - "Cross-signing and secret storage are not yet set up.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko zatiaľ nie sú nastavené.", + "Cross-signing and secret storage are enabled.": "Krížové podpisovanie a bezpečné úložisko sú zapnuté.", + "Cross-signing and secret storage are not yet set up.": "Krížové podpisovanie a bezpečné úložisko zatiaľ nie sú nastavené.", "Bootstrap cross-signing and secret storage": "Zaviesť podpisovanie naprieč zariadeniami a bezpečné úložisko", - "Cross-signing public keys:": "Verejné kľúče podpisovania naprieč zariadeniami:", + "Cross-signing public keys:": "Verejné kľúče krížového podpisovania:", "not found": "nenájdené", - "Cross-signing private keys:": "Súkromné kľúče podpisovania naprieč zariadeniami:", + "Cross-signing private keys:": "Súkromné kľúče krížového podpisovania:", "in secret storage": "na bezpečnom úložisku", "Secret storage public key:": "Verejný kľúč bezpečného úložiska:", "in account data": "v údajoch účtu", @@ -1475,13 +1475,13 @@ "Disconnect identity server": "Odpojiť server totožností", "Disconnect from the identity server ?": "Naozaj sa chcete odpojiť od servera totožností ?", "Disconnect": "Odpojiť", - "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Pred odpojením zo servera totožností by ste mali z neho odstrániť vaše osobné údaje. Žiaľ, server momentálne nie je dostupný a nie je možné sa k nemu pripojiť.", + "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Pred odpojením by ste mali odstrániť vaše osobné údaje zo servera totožností . Žiaľ, server totožnosti momentálne nie je dostupný a nie je možné sa k nemu pripojiť.", "You should:": "Mali by ste:", "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "Skontrolovať rozšírenia inštalované vo webovom prehliadači, ktoré by mohli blokovať prístup k serveru totožností (napr. rozšírenie Privacy Badger)", "contact the administrators of identity server ": "Kontaktovať správcu servera totožností ", "wait and try again later": "Počkať a skúsiť znovu neskôr", "Disconnect anyway": "Napriek tomu sa odpojiť", - "You are still sharing your personal data on the identity server .": "na servery máte stále uložené vaše osobné údaje.", + "You are still sharing your personal data on the identity server .": "Stále zdielate vaše osobné údaje so serverom totožnosti .", "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Odporúčame, aby ste ešte pred odpojením sa zo servera totožností odstránili vašu emailovú adresu a telefónne číslo.", "Identity Server (%(server)s)": "Server totožností (%(server)s)", "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Momentálne na vyhľadávanie kontaktov a na možnosť byť nájdení kontaktmi ktorých poznáte používate . Zmeniť server totožností môžete nižšie.", @@ -1496,7 +1496,7 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Použiť integračný server na správu botov, widgetov a balíčkov s nálepkami.", "Manage integrations": "Spravovať integrácie", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integračné servery zhromažďujú údaje nastavení, môžu spravovať widgety, odosielať vo vašom mene pozvánky alebo meniť úroveň moci.", - "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Súhlas s podmienkami používania servera totožností (%(serverName)s), aby ste mohli byť nájdení zadaním emailovej adresy alebo telefónneho čísla.", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Súhlaste s podmienkami používania servera totožností (%(serverName)s), aby ste mohli byť nájdení zadaním emailovej adresy alebo telefónneho čísla.", "Discovery": "Objaviť", "Deactivate account": "Deaktivovať účet", "Clear cache and reload": "Vymazať vyrovnávaciu pamäť a načítať znovu", @@ -1531,7 +1531,7 @@ "Keep recovery passphrase in memory for this session": "Ponechať (dlhé) heslo pre obnovu zálohy v pamäti pre túto reláciu", "Enter recovery passphrase": "Zadajte (dlhé) heslo pre obnovu zálohy", "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Nemožno sa dostať do tajného úložiska. Prosím, overte, že ste zadali správne (dlhé) heslo pre obnovu zálohy.", - "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej šifrovanej histórií správ a vašemu krížom-podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej šifrovanej histórií správ a vašej krížom podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy.", "Encryption upgrade available": "Je dostupná aktualizácia šifrovania", "Set up encryption": "Nastaviť šifrovanie", "Review where you’re logged in": "Zobraziť, kde ste prihlásený", @@ -1550,7 +1550,52 @@ "Unknown (user, session) pair:": "Neznámy pár (používateľ, relácia):", "Session already verified!": "Relácia je už overená!", "WARNING: Session already verified, but keys do NOT MATCH!": "VAROVANIE: Relácia je už overená, ale kľúče sa NEZHODUJÚ!", - "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Pokiaľ ste zabudli na vaše (dlhé) heslo na obnovu zálohy, môžete použiť váš kľúč na obnovu zálohy alebo nastaviť nové spôsoby obnovy zálohy.", + "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Pokiaľ ste zabudli vaše (dlhé) heslo na obnovu zálohy, môžete použiť váš kľúč na obnovu zálohy alebo nastaviť nové spôsoby obnovy zálohy.", "Incorrect recovery passphrase": "Nesprávne (dlhé) heslo pre obnovu zálohy", - "Backup could not be decrypted with this recovery passphrase: please verify that you entered the correct recovery passphrase.": "Záloha nemohla byť rozšifrovaná pomocou tohto (dlhého) helsa na obnovu zálohy: prosím, overte, či ste zadali správne (dlhé) helso na obnovu zálohy." + "Backup could not be decrypted with this recovery passphrase: please verify that you entered the correct recovery passphrase.": "Záloha nemohla byť rozšifrovaná pomocou tohto (dlhého) helsa na obnovu zálohy: prosím, overte, či ste zadali správne (dlhé) helso na obnovu zálohy.", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "VAROVANIE: OVERENIE KĽÚČOV ZLYHALO! Podpisovaný kľúč používateľa %(userId)s a relácia %(deviceId)s je \"%(fprint)s\" čo nezodpovedá zadanému kľúču \"%(fingerprint)s\". Môže to znamenať, že vaša komunikácia je infiltrovaná!", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Zadaný podpísaný kľúč sa zhoduje s podpísaným kľúčom od relácie %(deviceId)s používateľa %(userId)s. Relácia je označená ako overená.", + "Displays information about a user": "Zobrazuje informácie o používateľovi", + "Send a bug report with logs": "Zaslať chybové hlásenie so záznamami", + "Opens chat with the given user": "Otvorí konverzáciu s daným používateľom", + "Sends a message to the given user": "Pošle správu danému používateľovi", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s zmenil/a meno miestnosti z %(oldRoomName)s na %(newRoomName)s.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s pridal/a alternatívne adresy %(addresses)s pre túto miestnosť.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s pridal/a alternatívnu adresu %(addresses)s pre túto miestnosť.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s odstránil/a alternatívne adresy %(addresses)s pre túto miestnosť.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s odstránil/a alternatívnu adresu %(addresses)s pre túto miestnosť.", + "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s zmenil/a alternatívne adresy pre túto miestnosť.", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s zmenil hlavnú a alternatívne adresy pre túto miestnosť.", + "%(senderName)s changed the addresses for this room.": "%(senderName)s zmenil/a adresy pre túto miestnosť.", + "You signed in to a new session without verifying it:": "Prihlásili ste sa do novej relácie bez jej overenia:", + "Verify your other session using one of the options below.": "Overte svoje ostatné relácie pomocou jednej z nižšie uvedených možností.", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) sa prihlásil do novej relácie bez jej overenia:", + "Ask this user to verify their session, or manually verify it below.": "Poproste tohto používateľa, aby si overil svoju reláciu alebo ju nižšie manuálne overte.", + "Not Trusted": "Nedôveryhodné", + "Manually Verify by Text": "Manuálne overte pomocou textu", + "Interactively verify by Emoji": "Interaktívne overte pomocou emotikonov", + "Done": "Hotovo", + "a few seconds ago": "pred pár sekundami", + "about a minute ago": "približne pred minutou", + "about an hour ago": "približne pred hodinou", + "about a day ago": "približne deň dozadu", + "a few seconds from now": "o pár sekúnd", + "about a minute from now": "približne o minutu", + "about an hour from now": "približne o hodinu", + "about a day from now": "približne o deň", + "Support adding custom themes": "Umožniť pridávať vlastný vzhľad", + "Enable cross-signing to verify per-user instead of per-session": "Povoliť krížové podpisovanie na overovanie používateľa namiesto overovania jednotlivých relácií", + "Your homeserver does not support cross-signing.": "Váš domovský server nepodporuje krížové podpisovanie.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Váš účet má krížovo podpísanú identitu v bezpečnom úložisku, ale zatiaľ nie je nedôveryhodná pre túto reláciu.", + "Reset cross-signing and secret storage": "Obnoviť krížové podpisovanie a bezpečné úložisko", + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individuálne overte každú používateľskú reláciu a označte ju za dôveryhodnú, bez dôvery krížovo podpísaných zariadení.", + "Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Zálohovací kľúč je uložený v bezpečnom úložisku, ale jeho načítanie nie je povolené v tejto relácií. Prosím, zapnite krížové podpisovanie v Experimentoch, aby ste mohli modifikovať stav zálohy.", + "Cross-signing": "Krížové podpisovanie", + "Destroy cross-signing keys?": "Zmazať kľúče pre krížové podpisovanie?", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Zmazanie kľúčov pre krížové podpisovanie je nenávratné. Každý, s kým ste sa overili, bude vidieť bezpečnostné upozornenia. Toto určite nechcete robiť dokiaľ ste nestratili všetky zariadenia, s ktorými by ste mohli krížovo podpisovať.", + "Clear cross-signing keys": "Zmazať kľúče pre krížové podpisovanie", + "a new cross-signing key signature": "nový podpis kľúča pre krížové podpisovanie", + "a device cross-signing signature": "podpis krížovo podpísaného zariadenia", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Získajte prístup k vašej šifrovanej histórií správ a vašej krížom podpísanej identite pre overenie iných relácií zadaním vášho kľúču obnovy.", + "or another cross-signing capable Matrix client": "alebo iný Matrixový klient podporujúci krížové podpisovanie" } From 406839e89a43a559e6529c32998e40f8d5a77f21 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 May 2020 11:13:57 +0100 Subject: [PATCH 183/399] Upgrade matrix-js-sdk to 6.1.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8338a91bf7..99e0399d8e 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "is-ip": "^2.0.0", "linkifyjs": "^2.1.6", "lodash": "^4.17.14", - "matrix-js-sdk": "6.1.0-rc.1", + "matrix-js-sdk": "6.1.0", "minimist": "^1.2.0", "pako": "^1.0.5", "parse5": "^5.1.1", diff --git a/yarn.lock b/yarn.lock index d05c444470..0edea5433e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5720,10 +5720,10 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@6.1.0-rc.1: - version "6.1.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-6.1.0-rc.1.tgz#521d5683d10e49bb437b2457d1f0c6696fc7a4e3" - integrity sha512-q3XScyroUwY3qTBglRgE1lldfrLlSML5nU4gJzXrEwn2gGpR1P4IU8aT8EcumyOmeZhrrysS2UEE9fWgUmhkBw== +matrix-js-sdk@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-6.1.0.tgz#c28ad67c113c4aa9c8bce409c7ba550170bdc2ee" + integrity sha512-N+vCgxWORvhh7AGyWZlU5Z2brojbbnHnWlMkBF6JjWe6a+pfpjmRKp5/jeQpOz6yfe56sIQvU7ikBZl3JjlMiw== dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From bc8a53a7d797a513bf0fb2c000fd63cc7e90f4a8 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 May 2020 11:21:59 +0100 Subject: [PATCH 184/399] Prepare changelog for v2.6.0 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c34bfba87b..7901062b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +Changes in [2.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.0) (2020-05-19) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.6.0-rc.1...v2.6.0) + + * Upgrade to JS SDK 6.1.0 + * Revert "ImageView make clicking off it easier" + [\#4602](https://github.com/matrix-org/matrix-react-sdk/pull/4602) + * Remove debugging that causes email addresses to load forever (to release) + [\#4598](https://github.com/matrix-org/matrix-react-sdk/pull/4598) + Changes in [2.6.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.0-rc.1) (2020-05-14) ============================================================================================================= [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.5.0...v2.6.0-rc.1) From 26e6447be5331614668b0d0e3ae516a0988ef9bc Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 May 2020 11:22:00 +0100 Subject: [PATCH 185/399] v2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 99e0399d8e..28f3d1633c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.6.0-rc.1", + "version": "2.6.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 82ca614fa78958172e8d855e8fd37ca220f34d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 19 May 2020 06:50:47 +0000 Subject: [PATCH 186/399] Translated using Weblate (Estonian) Currently translated at 53.6% (1239 of 2311 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 3fb069055c..3d4b7ec888 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1236,5 +1236,11 @@ "Nothing appearing? Not all clients support interactive verification yet. .": "Mitte midagi ei kuvata? Kõik Matrix'i kliendid ei toeta veel interaktiivset verifitseerimist. .", "Waiting for %(userId)s to confirm...": "Ootan kinnitust kasutajalt %(userId)s…", "Skip": "Jäta vahele", - "Token incorrect": "Vigane tunnusluba" + "Token incorrect": "Vigane tunnusluba", + "%(oneUser)schanged their name %(count)s times|one": "Kasutaja %(oneUser)s muutis oma nime", + "Are you sure you want to deactivate your account? This is irreversible.": "Kas sa oled kindel, et soovid oma konto sulgeda? Seda tegevust ei saa hiljem tagasi pöörata.", + "Confirm account deactivation": "Kinnita konto sulgemine", + "There was a problem communicating with the server. Please try again.": "Serveriühenduses tekkis viga. Palun proovi uuesti.", + "Server did not return valid authentication information.": "Serveri saadetud vastuses ei olnud kehtivat autentimisteavet.", + "Please fill why you're reporting.": "Palun kirjelda veateate põhjust." } From 833eccf5cd901d04ddfcc06a7f95e2e2ebe6f5d5 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 May 2020 11:25:07 +0100 Subject: [PATCH 187/399] Reset matrix-js-sdk back to develop branch --- package.json | 2 +- yarn.lock | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3bdda8a583..7c008d5ccc 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "is-ip": "^2.0.0", "linkifyjs": "^2.1.6", "lodash": "^4.17.14", - "matrix-js-sdk": "6.1.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "minimist": "^1.2.0", "pako": "^1.0.5", "parse5": "^5.1.1", diff --git a/yarn.lock b/yarn.lock index b7e5017fd7..93118dab22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1285,6 +1285,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/qrcode@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" + integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== + dependencies: + "@types/node" "*" + "@types/react@*": version "16.9.35" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368" @@ -1293,13 +1300,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/qrcode@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" - integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== - dependencies: - "@types/node" "*" - "@types/react@16.9": version "16.9.32" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" @@ -5753,10 +5753,9 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@6.1.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "6.1.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-6.1.0.tgz#c28ad67c113c4aa9c8bce409c7ba550170bdc2ee" - integrity sha512-N+vCgxWORvhh7AGyWZlU5Z2brojbbnHnWlMkBF6JjWe6a+pfpjmRKp5/jeQpOz6yfe56sIQvU7ikBZl3JjlMiw== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e3c6a0e1a08a3812ba988e60eb5a2a013bb27404" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From 3cd03812022ee7d9b7b7fbc9eca2b09ba22517e4 Mon Sep 17 00:00:00 2001 From: rkfg Date: Tue, 19 May 2020 10:34:45 +0000 Subject: [PATCH 188/399] Translated using Weblate (Russian) Currently translated at 89.8% (2075 of 2311 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 3591d96120..be9c6bfdd4 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -97,13 +97,13 @@ "Who can read history?": "Кто может читать историю?", "You do not have permission to post to this room": "Вы не можете писать в эту комнату", "You have no visible notifications": "Нет видимых уведомлений", - "%(targetName)s accepted an invitation.": "%(targetName)s принял приглашение.", - "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s принял приглашение от %(displayName)s.", + "%(targetName)s accepted an invitation.": "%(targetName)s принимает приглашение.", + "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s принимает приглашение от %(displayName)s.", "Active call": "Активный вызов", "%(senderName)s answered the call.": "%(senderName)s ответил(а) на звонок.", - "%(senderName)s banned %(targetName)s.": "%(senderName)s заблокировал(а) %(targetName)s.", + "%(senderName)s banned %(targetName)s.": "%(senderName)s забанил(а) %(targetName)s.", "Call Timeout": "Нет ответа", - "%(senderName)s changed their profile picture.": "%(senderName)s изменил(а) свой аватар.", + "%(senderName)s changed their profile picture.": "%(senderName)s изменяет свой аватар.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s изменил(а) уровни прав %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s изменил(а) название комнаты на %(roomName)s.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s изменил(а) тему комнаты на \"%(topic)s\".", @@ -223,8 +223,8 @@ "Reason": "Причина", "%(targetName)s rejected the invitation.": "%(targetName)s отклонил(а) приглашение.", "Reject invitation": "Отклонить приглашение", - "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s удалил(а) свое отображаемое имя (%(oldDisplayName)s).", - "%(senderName)s removed their profile picture.": "%(senderName)s удалил(а) свой аватар.", + "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s удаляет своё отображаемое имя (%(oldDisplayName)s).", + "%(senderName)s removed their profile picture.": "%(senderName)s удаляет свой аватар.", "%(senderName)s requested a VoIP conference.": "%(senderName)s хочет начать конференц-звонок.", "Riot does not have permission to send you notifications - please check your browser settings": "У Riot нет разрешения на отправку уведомлений — проверьте настройки браузера", "Riot was not given permission to send notifications - please try again": "Riot не получил разрешение на отправку уведомлений, пожалуйста, попробуйте снова", @@ -302,8 +302,8 @@ "Server may be unavailable, overloaded, or you hit a bug.": "Возможно, сервер недоступен, перегружен или случилась ошибка.", "Server unavailable, overloaded, or something else went wrong.": "Возможно, сервер недоступен, перегружен или что-то еще пошло не так.", "Session ID": "ID сессии", - "%(senderName)s set a profile picture.": "%(senderName)s установил(а) себе аватар.", - "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s изменил(а) отображаемое имя на %(displayName)s.", + "%(senderName)s set a profile picture.": "%(senderName)s устанавливает себе аватар.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s меняет отображаемое имя на %(displayName)s.", "Signed Out": "Выполнен выход", "This room is not accessible by remote Matrix servers": "Это комната недоступна из других серверов Matrix", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Попытка загрузить выбранный интервал истории чата этой комнаты не удалась, так как у вас нет разрешений на просмотр.", @@ -696,7 +696,7 @@ "This room is not public. You will not be able to rejoin without an invite.": "Эта комната не является публичной. Вы не сможете войти без приглашения.", "Community IDs cannot be empty.": "ID сообществ не могут быть пустыми.", "In reply to ": "В ответ на ", - "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s изменил(а) отображаемое имя на %(displayName)s.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s меняет отображаемое имя на %(displayName)s.", "Failed to set direct chat tag": "Не удалось установить тег прямого чата", "Failed to remove tag %(tagName)s from room": "Не удалось удалить тег %(tagName)s из комнаты", "Failed to add tag %(tagName)s to room": "Не удалось добавить тег %(tagName)s в комнату", @@ -977,7 +977,7 @@ "Render simple counters in room header": "Отображать простые счетчики в заголовке комнаты", "Enable Emoji suggestions while typing": "Включить предложения смайликов при наборе", "Show a placeholder for removed messages": "Показывать плашки вместо удалённых сообщений", - "Show join/leave messages (invites/kicks/bans unaffected)": "Показывать сообщения о вступлении | выходе (не влияет на приглашения, исключения и запреты)", + "Show join/leave messages (invites/kicks/bans unaffected)": "Показывать сообщения о входе/выходе (не влияет на приглашения, кики и баны)", "Show avatar changes": "Показывать изменения аватара", "Show display name changes": "Показывать изменения отображаемого имени", "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Напоминать включить Безопасное Восстановление Сообщений в зашифрованных комнатах", @@ -1639,7 +1639,7 @@ "This alias is already in use": "Этот псевдоним уже используется", "Close dialog": "Закрыть диалог", "Please enter a name for the room": "Пожалуйста, введите название комнаты", - "This room is private, and can only be joined by invitation.": "Эта комната приватная и может быть присоединена только по приглашению.", + "This room is private, and can only be joined by invitation.": "Эта комната приватная, в неё можно войти только по приглашению.", "Hide advanced": "Скрыть расширения", "Show advanced": "Показать расширения", "Please fill why you're reporting.": "Пожалуйста, заполните, почему вы сообщаете.", From af2eed2228dbf7552c94fe10e970d8a4af88640d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 19 May 2020 13:17:34 +0100 Subject: [PATCH 189/399] Fix room alias lookup vs peeking race condition Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 24 ++++++++++++++++++++---- src/stores/RoomViewStore.js | 12 ++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 635597db74..8e343fe08f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -164,6 +164,8 @@ export default createReactClass({ canReact: false, canReply: false, + + matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), }; }, @@ -232,7 +234,8 @@ export default createReactClass({ initialEventId: RoomViewStore.getInitialEventId(), isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(), forwardingEvent: RoomViewStore.getForwardingEvent(), - shouldPeek: RoomViewStore.shouldPeek(), + // we should only peek once we have a ready client + shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(), showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", roomId), showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), }; @@ -681,6 +684,16 @@ export default createReactClass({ }); } break; + case 'sync_state': + if (!this.state.matrixClientIsReady) { + this.setState({ + matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), + }, () => { + // send another "initial" RVS update to trigger peeking if needed + this._onRoomViewStoreUpdate(true); + }); + } + break; } }, @@ -1663,14 +1676,16 @@ export default createReactClass({ const ErrorBoundary = sdk.getComponent("elements.ErrorBoundary"); if (!this.state.room) { - const loading = this.state.roomLoading || this.state.peekLoading; + const loading = this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading; if (loading) { + // Assume preview loading if we don't have a ready client or a room ID (still resolving the alias) + const previewLoading = !this.state.matrixClientIsReady || !this.state.roomId || this.state.peekLoading; return (
    - Date: Tue, 19 May 2020 15:40:26 +0100 Subject: [PATCH 190/399] Remove SSSS key upgrade check from rageshake This code doesn't exist anymore as the SSSS symmetric upgrade stuff has been removed. Fixes https://github.com/vector-im/riot-web/issues/13715 --- src/rageshake/submit-rageshake.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index e5027e0d37..9f9d7898cb 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -133,7 +133,6 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp body.append("cross_signing_supported_by_hs", String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"))); body.append("cross_signing_ready", String(await client.isCrossSigningReady())); - body.append("ssss_key_needs_upgrade", String(await client.secretStorageKeyNeedsUpgrade())); } } From dda0d2102c16b24b651c058c5a28f2caccc3318c Mon Sep 17 00:00:00 2001 From: Tirifto Date: Tue, 19 May 2020 13:00:53 +0000 Subject: [PATCH 191/399] Translated using Weblate (Esperanto) Currently translated at 100.0% (2312 of 2312 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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 3f749ebe0f..2729248bb0 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2409,5 +2409,6 @@ "QR Code": "Rapidresponda kodo", "Dismiss read marker and jump to bottom": "Forigi legomarkon kaj iri al fundo", "Jump to oldest unread message": "Iri al plej malnova nelegita mesaĝo", - "Upload a file": "Alŝuti dosieron" + "Upload a file": "Alŝuti dosieron", + "Create room": "Krei ĉambron" } From 894412ca4c32299630f2633e557e9ef4496a00cd Mon Sep 17 00:00:00 2001 From: Kim Brose Date: Tue, 19 May 2020 14:51:57 +0000 Subject: [PATCH 192/399] Translated using Weblate (German) Currently translated at 99.8% (2308 of 2312 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 1166e7710a..eaefbdaa71 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1103,7 +1103,7 @@ "Room information": "Rauminformationen", "Internal room ID:": "Interne Raum ID:", "Room version": "Raum Version", - "Room version:": "Raum Version:", + "Room version:": "Raum-Version:", "Developer options": "Entwickleroptionen", "General": "Allgemein", "Set a new account password...": "Neues Benutzerkonto-Passwort festlegen...", From 286828b3bb8a3556726c625be4b6034c1e83d87b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 19 May 2020 16:15:13 +0100 Subject: [PATCH 193/399] Disable irc mode in notifiactions panel --- src/components/structures/MessagePanel.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index cac04d84f1..93e4668f66 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -110,15 +110,16 @@ export default class MessagePanel extends React.Component { showReactions: PropTypes.bool, }; - constructor() { - super(); + // Force props to be loaded for useIRCLayout + constructor(props) { + super(props); this.state = { // previous positions the read marker has been in, so we can // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -203,10 +204,15 @@ export default class MessagePanel extends React.Component { onLayoutChange = () => { this.setState({ - useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }); } + useIRCLayout(ircLayoutSelected) { + // if room is null we are not in a normal room list + return ircLayoutSelected && this.props.room; + } + /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { From 86ad6de41ee455285695270242a5d0983b014899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 20 May 2020 11:01:56 +0200 Subject: [PATCH 194/399] EventIndex: Handle null tokens in the crawler loop as well. This is similar to 5eb510387c9aa08a061d5fd17bed3efc104b48eb. But now the checkpoint arrived during a crawl. --- src/indexing/EventIndex.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 02151f8474..16b0183da1 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -489,14 +489,20 @@ export default class EventIndex extends EventEmitter { return object; }); - // Create a new checkpoint so we can continue crawling the room for - // messages. - const newCheckpoint = { - roomId: checkpoint.roomId, - token: res.end, - fullCrawl: checkpoint.fullCrawl, - direction: checkpoint.direction, - }; + let newCheckpoint; + + // The token can be null for some reason. Don't create a checkpoint + // in that case since adding it to the db will fail. + if (res.end) { + // Create a new checkpoint so we can continue crawling the room + // for messages. + newCheckpoint = { + roomId: checkpoint.roomId, + token: res.end, + fullCrawl: checkpoint.fullCrawl, + direction: checkpoint.direction, + }; + } try { for (let i = 0; i < redactionEvents.length; i++) { @@ -506,6 +512,15 @@ export default class EventIndex extends EventEmitter { const eventsAlreadyAdded = await indexManager.addHistoricEvents( events, newCheckpoint, checkpoint); + + // We didn't get a valid new checkpoint from the server, nothing + // to do here anymore. + if (!newCheckpoint) { + console.log("EventIndex: The server didn't return a valid ", + "new checkpoint, not continuing the crawl.", checkpoint); + continue + } + // If all events were already indexed we assume that we catched // up with our index and don't need to crawl the room further. // Let us delete the checkpoint in that case, otherwise push From db72c645bae492a5a3daba70695ef137ef07d485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 20 May 2020 11:14:37 +0200 Subject: [PATCH 195/399] EventIndex: Add a missing semicolon. --- src/indexing/EventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 16b0183da1..d372c38405 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -518,7 +518,7 @@ export default class EventIndex extends EventEmitter { if (!newCheckpoint) { console.log("EventIndex: The server didn't return a valid ", "new checkpoint, not continuing the crawl.", checkpoint); - continue + continue; } // If all events were already indexed we assume that we catched From 3e30df17fbc9a3a5efeb4c7bbf24ea62c828b235 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 13:07:33 +0100 Subject: [PATCH 196/399] Slider is more responsive --- res/css/views/elements/_Slider.scss | 5 ++++- src/components/views/elements/Slider.tsx | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 06c3c4c98b..58ba2813b4 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -38,7 +38,9 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2em solid $slider-background-color; + height: 0.4em; + background-color: $slider-background-color; + border: 0; } .mx_Slider_selection { @@ -47,6 +49,7 @@ limitations under the License. width: calc(100% - 1em); // 2 * half the width of a dot height: 1em; position: absolute; + pointer-events: none; } .mx_Slider_selectionDot { diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index e181f0d9e3..f76a4684d3 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -93,7 +93,7 @@ export default class Slider extends React.Component { return
    -
    +
    {} : this.onClick.bind(this)}/> { selection }
    @@ -102,6 +102,15 @@ export default class Slider extends React.Component {
    ; } + + onClick(event: React.MouseEvent) { + const width = (event.target as HTMLElement).clientWidth; + // nativeEvent is safe to use because https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX + // is supported by all modern browsers + const relativeClick = (event.nativeEvent.offsetX / width); + const nearestValue = this.props.values[Math.round(relativeClick * (this.props.values.length - 1))]; + this.props.onSelectionChange(nearestValue); + } } interface IDotProps { From 55e72dd5bf826fd8fb9cfb3f7b769ad682247b67 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 13:45:54 +0100 Subject: [PATCH 197/399] Remove min and max font setting --- src/FontWatcher.js | 9 +++++---- .../settings/tabs/user/AppearanceUserSettingsTab.js | 6 +++--- src/settings/Settings.js | 10 ---------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 561edc4662..1128ac1bd5 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,6 +18,10 @@ import dis from './dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { + + static minSize = 13; + static maxSize = 20; + constructor() { this._dispatcherRef = null; } @@ -38,10 +42,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const min = SettingsStore.getValue("fontSizeMin"); - const max = SettingsStore.getValue("fontSizeMax"); - - const fontSize = Math.max(Math.min(max, size), min); + const fontSize = Math.max(Math.min(this.maxSize, size), this.minSize); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 63857ed9c2..308b7098d1 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -25,7 +25,7 @@ import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; -import _range from "lodash/range"; +import { FontWatcher } from "../../../../../FontWatcher"; export default class AppearanceUserSettingsTab extends React.Component { constructor() { @@ -109,8 +109,8 @@ export default class AppearanceUserSettingsTab extends React.Component { console.log({value}); const parsedSize = parseFloat(value); - const min = SettingsStore.getValue("fontSizeMin"); - const max = SettingsStore.getValue("fontSizeMax"); + const min = FontWatcher.minSize; + const max = FontWatcher.maxSize; if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; diff --git a/src/settings/Settings.js b/src/settings/Settings.js index afe8d2cecc..5f093f6a70 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -171,16 +171,6 @@ export const SETTINGS = { default: 16, controller: new FontSizeController(), }, - "fontSizeMin": { - displayName: _td("Min font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 13, - }, - "fontSizeMax": { - displayName: _td("Max font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 20, - }, "useCustomFontSize": { displayName: _td("Custom font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 82b76192aee6fe64865c1bc4ff456d0c4614dad0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 14:44:56 +0100 Subject: [PATCH 198/399] Fixes, lints and i18n --- src/FontWatcher.js | 4 ++-- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 -- src/settings/controllers/FontSizeController.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 1128ac1bd5..b45d9065ce 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { @@ -42,7 +42,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const fontSize = Math.max(Math.min(this.maxSize, size), this.minSize); + const fontSize = Math.max(Math.min(FontWatcher.maxSize, size), FontWatcher.minSize); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 308b7098d1..3d04e10df7 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -24,7 +24,7 @@ import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import { FontWatcher } from "../../../../../FontWatcher"; export default class AppearanceUserSettingsTab extends React.Component { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d6670e85b3..b7417762f1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -412,8 +412,6 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Font size": "Font size", - "Min font size": "Min font size", - "Max font size": "Max font size", "Custom font size": "Custom font size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js index 8e855e31ec..3ef01ab99b 100644 --- a/src/settings/controllers/FontSizeController.js +++ b/src/settings/controllers/FontSizeController.js @@ -15,7 +15,7 @@ limitations under the License. */ import SettingController from "./SettingController"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; export default class FontSizeController extends SettingController { constructor() { From 4e9a139e8b45a8597b3fa51c357fca5ea1691fa7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 14:49:18 +0100 Subject: [PATCH 199/399] lint --- src/FontWatcher.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index b45d9065ce..c547ef8ce0 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,7 +18,6 @@ import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - static minSize = 13; static maxSize = 20; From adec5a4f921bb9e24d7f0d52240bbcc84959b871 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 15:09:10 +0100 Subject: [PATCH 200/399] fix test --- test/components/views/messages/TextualBody-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 59671327ce..4e93b3bb64 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,7 @@ describe("", () => { 'Hey ' + '' + 'Member' + ''); }); From 50a44405f012f9f987709025d1ea4c0e1e275eac Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 20 May 2020 15:17:47 +0100 Subject: [PATCH 201/399] CONSTANT_CASING --- src/FontWatcher.js | 6 +++--- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index c547ef8ce0..006df202ad 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,8 +18,8 @@ import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - static minSize = 13; - static maxSize = 20; + static MIN_SIZE = 13; + static MAX_SIZE = 20; constructor() { this._dispatcherRef = null; @@ -41,7 +41,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const fontSize = Math.max(Math.min(FontWatcher.maxSize, size), FontWatcher.minSize); + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 3d04e10df7..5b49dd0abd 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -109,8 +109,8 @@ export default class AppearanceUserSettingsTab extends React.Component { console.log({value}); const parsedSize = parseFloat(value); - const min = FontWatcher.minSize; - const max = FontWatcher.maxSize; + const min = FontWatcher.MIN_SIZE; + const max = FontWatcher.MAX_SIZE; if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; From b0e0073970a904cdbf7b1cd8120c5b616b4f9029 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 20 May 2020 08:13:27 +0000 Subject: [PATCH 202/399] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2314 of 2314 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 4995eeccb0..f222d7d09f 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2434,5 +2434,8 @@ "QR Code": "QR Code", "Dismiss read marker and jump to bottom": "取消讀取標記並跳至底部", "Jump to oldest unread message": "跳至最舊的未讀訊息", - "Upload a file": "上傳檔案" + "Upload a file": "上傳檔案", + "Use IRC layout": "使用 IRC 佈局", + "IRC display name width": "IRC 顯示名稱寬度", + "Create room": "建立聊天室" } From 48a66a817fe6f6324bd5dc863baf8ae7bbaf5067 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Wed, 20 May 2020 10:20:52 +0000 Subject: [PATCH 203/399] Translated using Weblate (Esperanto) Currently translated at 100.0% (2314 of 2314 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 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 2729248bb0..56b5d53008 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2410,5 +2410,7 @@ "Dismiss read marker and jump to bottom": "Forigi legomarkon kaj iri al fundo", "Jump to oldest unread message": "Iri al plej malnova nelegita mesaĝo", "Upload a file": "Alŝuti dosieron", - "Create room": "Krei ĉambron" + "Create room": "Krei ĉambron", + "Use IRC layout": "Uzi aranĝon de IRC", + "IRC display name width": "Larĝo de vidiga nomo de IRC" } From 402c1b0d65d097915bca5faf4b43715606c94b8c Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Wed, 20 May 2020 04:37:45 +0000 Subject: [PATCH 204/399] Translated using Weblate (Finnish) Currently translated at 94.3% (2183 of 2314 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 | 77 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index ae08572eb2..bf2566990c 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -2189,5 +2189,80 @@ "Successfully restored %(sessionCount)s keys": "%(sessionCount)s avaimen palautus onnistui", "This requires the latest Riot on your other devices:": "Tämä vaatii uusimman Riotin muilla laitteillasi:", "Currently indexing: %(currentRoom)s": "Indeksoidaan huonetta: %(currentRoom)s", - "Jump to oldest unread message": "Siirry vanhimpaan lukemattomaan viestiin" + "Jump to oldest unread message": "Siirry vanhimpaan lukemattomaan viestiin", + "Opens chat with the given user": "Avaa keskustelun annetun käyttäjän kanssa", + "Sends a message to the given user": "Lähettää viestin annetulle käyttäjälle", + "Manually Verify by Text": "Varmenna käsin tekstillä", + "Interactively verify by Emoji": "Varmenna interaktiivisesti emojilla", + "Use IRC layout": "Käytä IRC-asettelua", + "Enable cross-signing to verify per-user instead of per-session": "Ota ristivarmennus käyttöön varmentaaksesi käyttäjät istuntojen sijaan", + "Keep recovery passphrase in memory for this session": "Pidä palautuksen salalause muistissa tämän istunnon ajan", + "Manually verify all remote sessions": "Varmenna kaikki etäistunnot käsin", + "IRC display name width": "IRC-näyttönimen leveys", + "Verify this session by confirming the following number appears on its screen.": "Varmenna tämä istunto varmistamalla, että seuraava numero ilmestyy sen näytölle.", + "Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…": "Odotetaan toista istuntoasi, %(deviceName)s (%(deviceId)s), varmennukseen…", + "Waiting for your other session to verify…": "odotetaan toista istuntoasi varmennukseen…", + "Verify all your sessions to ensure your account & messages are safe": "Varmenna kaikki istuntosi varmistaaksesi, että tunnuksesi ja viestisi ovat turvassa", + "Verify the new login accessing your account: %(name)s": "Varmenna uusi tunnuksellesi sisäänkirjautunut taho: %(name)s", + "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Tällä hetkellä salasanan vaihtaminen nollaa kaikki osapuolten välisen salauksen avaimet kaikissa istunnoissa, tehden salatusta keskusteluhistoriasta lukukelvotonta, ellet ensin vie kaikkia huoneavaimiasi ja tuo niitä salasanan vaihtamisen jäkeen takaisin. Tulevaisuudessa tämä tulee toimimaan paremmin.", + "Your homeserver does not support cross-signing.": "Kotipalvelimesi ei tue ristivarmennusta.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Tunnuksellasi on ristivarmennuksen identiteetti salavarastossa, mutta tämä istunto ei luota siihen.", + "Reset cross-signing and secret storage": "Nollaa ristivarmennus ja salavarasto", + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Varmenna jokainen käyttäjän istunto erikseen, äläkä luota ristivarmennettuihin laitteisiin.", + "Securely cache encrypted messages locally for them to appear in search results.": "Pidä salatut viestit turvallisessa välimuistissa, jotta ne näkyvät hakutuloksissa.", + "Riot is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom Riot Desktop with search components added.": "Riotissa ei ole joitain komponentteja, joita tarvitaan viestien turvalliseen välimuistitallennukseen. Jos haluat kokeilla tätä ominaisuutta, käännä mukautettu Riot Desktop, jossa on mukana hakukomponentit.", + "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.": "Riot ei voi tallentaa viestejä turvalliseen välimuistiin pyöriessään selaimessa. Käytä Electron-pohjaista Riot Desktop-sovellusta nähdäksesi salatut viestit hakutuloksissa.", + "This session is backing up your keys. ": "Tämä istunto varmuuskopioi avaimesi. ", + "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Tämä istunto ei varmuuskopioi avaimiasi, mutta sillä on olemassaoleva varmuuskopio, jonka voit palauttaa ja lisätä jatkaaksesi.", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Yhdistä tämä istunto avainten varmuuskopiointiin ennen uloskirjautumista, jotta et menetä avaimia, jotka ovat vain tässä istunnossa.", + "Connect this session to Key Backup": "Yhdistä tämä istunto avainten varmuuskopiointiin", + "Backup has a valid signature from verified session ": "Varmuuskopiossa on kelvollinen allekirjoitus varmennetusta istunnosta ", + "Backup has a valid signature from unverified session ": "Varmuuskopiossa on kelvollinen allekirjoitus varmentamattomasta istunnosta ", + "Backup has an invalid signature from verified session ": "Varmuuskopiossa on epäkelpo allekirjoitus varmennetusta istunnosta ", + "Backup has an invalid signature from unverified session ": "Varmuuskopiossa on epäkelpo allekirjoitus varmentamattomasta istunnosta ", + "This backup is trusted because it has been restored on this session": "Tähän varmuuskopioon luotetaan, koska se on palautettu tässä istunnossa", + "Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Vara-avain on salavarastossa, mutta salavarasto ei ole käytössä tässä istunnossa. Ota ristivarmennus käyttöön Laboratoriosta muokkaaksesi avainten varmuuskopioinnin tilaa.", + "Your keys are not being backed up from this session.": "Avaimiasi ei varmuuskopioida tästä istunnosta.", + "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Salasanasi on onnistuneesti vaihdettu. Et saa ilmoituksia muilla laitteillasi ennen kuin kirjaudut niillä takaisin sisään", + "Invalid theme schema.": "Epäkelpo teeman skeema.", + "Custom theme URL": "Mukautettu teeman osoite", + "Keyboard Shortcuts": "Pikanäppäimet", + "Session ID:": "Istunnon tunnus:", + "Session key:": "Istunnon avain:", + "Where you’re logged in": "Missä olet sisäänkirjautuneena", + "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Muokkaa istuntojesi nimiä ja kirjaudu niistä ulos alapuolella tai varmenna ne käyttäjäprofiilissasi.", + "This user has not verified all of their sessions.": "Tämä käyttäjä ei ole varmentanut kaikkia istuntojaan.", + "You have not verified this user.": "Et ole varmentanut tätä käyttäjää.", + "You have verified this user. This user has verified all of their sessions.": "Olet varmentanut tämän käyttäjän. Tämä käyttäjä on varmentanut kaikki istuntonsa.", + "This room is end-to-end encrypted": "Tämä huone käyttää osapuolten välistä salausta", + "Everyone in this room is verified": "Kaikki tämän huoneen käyttäjät on varmennettu", + "Some sessions for this user are not trusted": "Osaan tämän käyttäjän istunnoista ei luoteta", + "All sessions for this user are trusted": "Kaikkiin tämän käyttäjän istunnoista luotetaan", + "Some sessions in this encrypted room are not trusted": "Osaan tämän salausta käyttävän huoneen istunnoista ei luoteta", + "All sessions in this encrypted room are trusted": "Kaikkiin tämän salausta käyttävän huoneen istuntoihin luotetaan", + "Your key share request has been sent - please check your other sessions for key share requests.": "Avainten jakopyyntösi on lähetetty. Tarkista muut istuntosi avainten jakopyyntöjen varalta.", + "Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Avainten jakopyynnöt lähetetään muille istunnoillesi automaattisesti. Jos hylkäsit tai jätit huomiotta avainten jakopyynnön toisessa istunnossasi, klikkaa tästä pyytääksesi avaimia uudelleen.", + "If your other sessions do not have the key for this message you will not be able to decrypt them.": "Jos muissa laitteissasi ei ole avainta tämän viestin purkamiseen, niillä istunnoilla ei voi lukea tätä viestiä.", + "Encrypted by an unverified session": "Salattu varmentamattoman istunnon toimesta", + "Encrypted by a deleted session": "Salattu poistetun istunnon toimesta", + "No sessions with registered encryption keys": "Yhdelläkään istunnolla ei ole rekisteröityjä salausavaimia", + "Create room": "Luo huone", + "Reject & Ignore user": "Hylkää ja jätä käyttäjä huomiotta", + "Start Verification": "Aloita varmennus", + "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Viestisi ovat turvattu, ja vain sinulla ja vastaanottajalla on avaimet viestien lukemiseen.", + "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.": "Salausta käyttävissä huoneissa viestisi on turvattu, ja vain sinulla ja vastaanottajilla on yksityiset avaimet viestien lukemiseen.", + "Verify User": "Varmenna käyttäjä", + "For extra security, verify this user by checking a one-time code on both of your devices.": "Lisäturvaksi, varmenna tämä käyttäjä tarkistamalla koodin kummankin laitteella.", + "The homeserver the user you’re verifying is connected to": "Käyttäjä, jota varmennat, on kotipalvelimella", + "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "Istunto, jota yrität varmentaa, ei tue QR-koodin skannausta tai emoji-varmennusta, joita Riot tukee. Kokeile eri asiakasohjelmalla.", + "Verify by scanning": "Varmenna skannaamalla", + "If you can't scan the code above, verify by comparing unique emoji.": "Jos et pysty skannaamaan yläpuolella olevaa koodia, varmenna vertaamalla emojia.", + "Verify by comparing unique emoji.": "Varmenna vertaamalla uniikkia emojia.", + "Verify by emoji": "Varmenna emojilla", + "Verify all users in a room to ensure it's secure.": "Varmenna kaikki huoneen käyttäjät varmistaaksesi, että se on turvallinen.", + "In encrypted rooms, verify all users to ensure it’s secure.": "Varmenna kaikki käyttäjät salausta käyttävissä huoneissa, jotta huone on varmasti turvallinen.", + "You've successfully verified your device!": "Olet onnistuneesti varmentanut laitteesi!", + "You've successfully verified %(deviceName)s (%(deviceId)s)!": "Olet onnistuneesti varmentanut laitteen %(deviceName)s (%(deviceId)s)!", + "You've successfully verified %(displayName)s!": "Olet varmentanut käyttäjän %(displayName)s!", + "Verified": "Varmennettu" } From a462a374cb716c35489db12441279147803cfce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 20 May 2020 06:30:49 +0000 Subject: [PATCH 205/399] Translated using Weblate (French) Currently translated at 100.0% (2314 of 2314 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index c231769f27..7a89f71b73 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2435,5 +2435,8 @@ "QR Code": "Code QR", "Dismiss read marker and jump to bottom": "Ignorer le signet de lecture et aller en bas", "Jump to oldest unread message": "Aller au plus vieux message non lu", - "Upload a file": "Envoyer un fichier" + "Upload a file": "Envoyer un fichier", + "Use IRC layout": "Utiliser la mise en page d’IRC", + "IRC display name width": "Largeur du nom affiché IRC", + "Create room": "Créer un salon" } From 1f75caf6d276c0e6f8c626a14a75c60dfd890389 Mon Sep 17 00:00:00 2001 From: Xose M Date: Wed, 20 May 2020 06:19:20 +0000 Subject: [PATCH 206/399] Translated using Weblate (Galician) Currently translated at 41.1% (950 of 2314 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 3328292be0..f3b9e38273 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -545,7 +545,7 @@ "Are you sure you want to leave the room '%(roomName)s'?": "Seguro que desexa saír da sala '%(roomName)s'?", "Failed to leave room": "Algo fallou ao saír da sala", "Signed Out": "Desconectada", - "For security, this session has been signed out. Please sign in again.": "Por seguridade, pechouse a sesión. Por favor, conéctese de novo.", + "For security, this session has been signed out. Please sign in again.": "Por seguridade, pechouse a sesión. Por favor, conéctate outra vez.", "Old cryptography data detected": "Detectouse o uso de criptografía sobre datos antigos", "Logout": "Desconectar", "Your Communities": "As súas Comunidades", @@ -621,10 +621,10 @@ "A new password must be entered.": "Debe introducir un novo contrasinal.", "New passwords must match each other.": "Os novos contrasinais deben ser coincidentes.", "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.": "Detectáronse datos de una versión anterior de Riot. Isto causará un mal funcionamento da criptografía extremo-a-extremo na versión antiga. As mensaxes cifradas extremo-a-extremo intercambiadas mentres utilizaba a versión anterior poderían non ser descifrables en esta versión. Isto tamén podería causar que mensaxes intercambiadas con esta versión tampouco funcionasen. Se ten problemas, desconéctese e conéctese de novo. Para manter o historial de mensaxes, exporte e reimporte as súas chaves.", - "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Enviouse un correo a %(emailAddress)s. Unha vez siga a ligazón que contén, pulse abaixo.", - "I have verified my email address": "Validei o meu enderezo de correo electrónico", + "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Enviouse un correo a %(emailAddress)s. Unha vez sigas a ligazón que contén, preme embaixo.", + "I have verified my email address": "Validei o meu enderezo de email", "Return to login screen": "Volver a pantalla de conexión", - "Send Reset Email": "Enviar correo electrónico de restablecemento", + "Send Reset Email": "Enviar email de restablecemento", "Incorrect username and/or password.": "Nome de usuaria ou contrasinal non válidos.", "Please note you are logging into the %(hs)s server, not matrix.org.": "Teña en conta que se está a conectar ao servidor %(hs)s, non a matrix.org.", "The phone number entered looks invalid": "O número de teléfono introducido non semella ser válido", @@ -834,7 +834,7 @@ "Off": "Off", "Riot does not know how to join a room on this network": "Riot non sabe como conectar cunha sala nesta rede", "Mentions only": "Só mencións", - "You can now return to your account after signing out, and sign in on other devices.": "Pode volver a súa contra tras desconectarse, e conectarse en outros dispositivos.", + "You can now return to your account after signing out, and sign in on other devices.": "Podes voltar a túa conta tras desconectarte, e conectarte noutros dispositivos.", "Enable email notifications": "Activar notificacións de correo", "Event Type": "Tipo de evento", "Download this file": "Descargue este ficheiro", @@ -860,7 +860,7 @@ "Clear Storage and Sign Out": "Limpar o almacenamento e Desconectar", "Refresh": "Actualizar", "We encountered an error trying to restore your previous session.": "Atopamos un fallo intentando restablecer a súa sesión anterior.", - "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Limpando o almacenamento do navegador podería resolver o problema, pero desconectarao e non poderá ler o historial cifrado da conversa.", + "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Limpando o almacenamento do navegador podería resolver o problema, pero desconectarate e non poderás ler o historial cifrado da conversa.", "Collapse Reply Thread": "Comprimir o fío de respostas", "e.g. %(exampleValue)s": "p.ex. %(exampleValue)s", "Send analytics data": "Enviar datos de análises", @@ -937,5 +937,30 @@ "Whether you're using Riot as an installed Progressive Web App": "Se estás a usar Riot como unha Progressive Web App instalada", "Your user agent": "User Agent do navegador", "The information being sent to us to help make Riot better includes:": "Información que nos envías para mellorar Riot inclúe:", - "Please install Chrome, Firefox, or Safari for the best experience.": "Instala Chrome, Firefox, ou Safari para ter unha mellor experiencia." + "Please install Chrome, Firefox, or Safari for the best experience.": "Instala Chrome, Firefox, ou Safari para ter unha mellor experiencia.", + "Sign In or Create Account": "Conéctate ou Crea unha Conta", + "Sign In": "Conectar", + "Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Confirma o borrado destas sesións ao usar Single Sign On como proba da túa identidade.", + "Confirm deleting these sessions by using Single Sign On to prove your identity.|one": "Confirma o borrado desta sesión ao utilizar Single Sign On como proba da túa identidade.", + "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Xestiona os nomes e pecha as sesións embaixo ou verificaas no teu Perfil de Usuaria.", + "Sign Up": "Rexistro", + "Sign in with single sign-on": "Conectar usando Single Sign On", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "O eliminación das chaves de sinatura cruzada é permanente. Calquera a quen verificases con elas verá alertas de seguridade. Seguramente non queres facer esto, a menos que perdeses todos os dispositivos nos que podías asinar.", + "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. ": "Usaches anteriormente unha versión máis recente de Riot en %(host)s. Para usar esta versión de novo con cifrado E2E, tes que desconectar e conectar outra vez. ", + "Confirm your account deactivation by using Single Sign On to prove your identity.": "Confirma a desactivación da túa conta usando Single Sign On para probar a túa identidade.", + "To continue, use Single Sign On to prove your identity.": "Para continuar, usa Single Sign On para probar a túa identidade.", + "Are you sure you want to sign out?": "Tes a certeza de querer desconectar?", + "If you didn’t sign in to this session, your account may be compromised.": "Se ti non iniciaches esta sesión a túa conta podería estar comprometida.", + "Sign out and remove encryption keys?": "Desconectar e eliminar as chaves de cifrado?", + "This will allow you to return to your account after signing out, and sign in on other sessions.": "Esto permitirache voltar a túa conta tras desconectar, e conectarte noutras sesións.", + "Sign in to your Matrix account on %(serverName)s": "Conecta a túa conta Matrix en %(serverName)s", + "Sign in to your Matrix account on ": "Conecta a túa conta Matrix en ", + "Sign in with SSO": "Conecta utilizando SSO", + "Sign in instead": "Conectar", + "A verification email will be sent to your inbox to confirm setting your new password.": "Ímosche enviar un email para confirmar o teu novo contrasinal.", + "Your password has been reset.": "Restableceuse o contrasinal.", + "Enter your password to sign in and regain access to your account.": "Escribe o contrasinal para conectarte e retomar o acceso a túa conta.", + "Sign in and regain access to your account.": "Conéctate e recupera o acceso a túa conta.", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Non podes conectar a conta. Contacta coa administración do teu servidor para máis información.", + "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Aviso: os teus datos personais (incluíndo chaves de cifrado) aínda están gardadas nesta sesión. Pechaa se remataches de usar esta sesión, ou se quere conectar con outra conta." } From 31ac5aaa77600f01492cf499cabce7a66c683bec Mon Sep 17 00:00:00 2001 From: random Date: Wed, 20 May 2020 09:27:36 +0000 Subject: [PATCH 207/399] Translated using Weblate (Italian) Currently translated at 100.0% (2314 of 2314 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 014d6015b3..359938ff3b 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2430,5 +2430,8 @@ "QR Code": "Codice QR", "Dismiss read marker and jump to bottom": "Scarta il segno di lettura e salta alla fine", "Jump to oldest unread message": "Salta al messaggio non letto più vecchio", - "Upload a file": "Invia un file" + "Upload a file": "Invia un file", + "Use IRC layout": "Usa il layout IRC", + "IRC display name width": "Larghezza nome di IRC", + "Create room": "Crea stanza" } From a40d795153a73095cc3ed81bd9f891ce28c48d59 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Wed, 20 May 2020 13:52:59 +0000 Subject: [PATCH 208/399] Translated using Weblate (Lithuanian) Currently translated at 45.8% (1059 of 2314 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 2652433075..c893034644 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -13,9 +13,9 @@ "The information being sent to us to help make Riot.im better includes:": "Informacija, siunčiama mums, kad padėtų tobulinti Riot.im, apima:", "Fetching third party location failed": "Nepavyko gauti trečios šalies vietos", "A new version of Riot is available.": "Yra prieinama nauja Riot versija.", - "I understand the risks and wish to continue": "Aš suprantu riziką ir noriu tęsti", + "I understand the risks and wish to continue": "Suprantu šią riziką ir noriu tęsti", "Send Account Data": "Siųsti paskyros duomenis", - "Advanced notification settings": "Sudėtingesni pranešimų nustatymai", + "Advanced notification settings": "Išplėstiniai pranešimų nustatymai", "Uploading report": "Išsiunčiama ataskaita", "Sunday": "Sekmadienis", "Guests can join": "Svečiai gali prisijungti", @@ -324,7 +324,7 @@ "Anyone who knows the room's link, including guests": "Bet kas, žinantis kambario nuorodą, įskaitant svečius", "Anyone": "Bet kas", "Permissions": "Leidimai", - "Advanced": "Sudėtingesni nustatymai", + "Advanced": "Išplėstiniai", "Add a topic": "Pridėti temą", "Local addresses for this room:": "Vietiniai šio kambario adresai:", "This room has no local addresses": "Šis kambarys neturi jokių vietinių adresų", @@ -1058,7 +1058,7 @@ "You'll lose access to your encrypted messages": "Jūs prarasite prieigą prie savo užšifruotų žinučių", "New session": "Naujas seansas", "Enter secret storage passphrase": "Įveskite slaptos saugyklos slaptafrazę", - "Enter recovery passphrase": "Įveskite atstatymo slaptafrazę", + "Enter recovery passphrase": "Įveskite atgavimo slaptafrazę", "Warning: you should only set up key backup from a trusted computer.": "Įspėjimas: atsarginę raktų kopiją sukurkite tik iš patikimo kompiuterio.", "Warning: You should only set up key backup from a trusted computer.": "Įspėjimas: Atsarginę raktų kopiją sukurkite tik iš patikimo kompiuterio.", "Server Name": "Serverio Pavadinimas", @@ -1073,5 +1073,25 @@ "For maximum security, this should be different from your account password.": "Maksimaliam saugumui užtikrinti ji turi skirtis nuo jūsų paskyros slaptažodžio.", "Enter a passphrase...": "Įveskite slaptafrazę...", "Please enter your passphrase a second time to confirm.": "Įveskite slaptafrazę antrą kartą, kad ją patvirtintumėte.", - "Secure your backup with a passphrase": "Apsaugokite savo atsarginę kopiją slaptafraze" + "Secure your backup with a passphrase": "Apsaugokite savo atsarginę kopiją slaptafraze", + "Set up encryption": "Nustatyti šifravimą", + "COPY": "Kopijuoti", + "Enter recovery key": "Įveskite atgavimo raktą", + "Keep going...": "Tęskite...", + "Please install Chrome, Firefox, or Safari for the best experience.": "Riot geriausiai veikia su Chrome, Firefox, arba Safari naršyklėmis.", + "Syncing...": "Sinchronizuojama...", + "Signing In...": "Prijungiama...", + "If you've joined lots of rooms, this might take a while": "Jei esate prisijungę prie daug kambarių, tai gali užtrukti", + "Without completing security on this session, it won’t have access to encrypted messages.": "Neužbaigus saugumo šioje sesijoje, ji neturės priėjimo prie šifruotų žinučių.", + "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Nustatykite atgavimo slaptafrazę, kad apsaugotumėte šifruotą informaciją ir atgautumėte ją jei atsijungsite. Ji turi skirtis nuo jūsų paskyros slaptažodžio:", + "Enter a recovery passphrase": "Įveskite atgavimo slaptafrazę", + "Back up encrypted message keys": "Padaryti atsargines šifruotų žinučių raktų kopijas", + "Set up with a recovery key": "Nustatyti su atgavimo raktu", + "Enter your recovery passphrase a second time to confirm it.": "Įveskite atgavimo slaptafrazę antrą kartą, kad ją patvirtintumėte.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Jūsų atgavimo raktas yra atsarginė saugumo priemonė - jūs galite jį naudoti priėjimo prie jūsų šifruotų žinučių atgavimui, jei pamiršite savo atgavimo slaptafrazę.", + "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Laikykite šio rakto kopiją saugioje vietoje, pavyzdžiui slaptažodžių tvarkyklėje arba seife.", + "Your recovery key": "Jūsų atgavimo raktas", + "Copy": "Kopijuoti", + "Make a copy of your recovery key": "Padaryti jūsų atgavimo rakto kopiją", + "Please enter your recovery passphrase a second time to confirm.": "Įveskite atgavimo slaptafrazę antrą kartą, kad patvirtintumėte." } From 5618c382bd4b974ee68e8010c71af1a4733c0600 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 20 May 2020 16:01:34 +0100 Subject: [PATCH 209/399] Update code style to mention switch statements --- code_style.md | 1 + 1 file changed, 1 insertion(+) diff --git a/code_style.md b/code_style.md index 3ad0d38873..01c1f37146 100644 --- a/code_style.md +++ b/code_style.md @@ -151,6 +151,7 @@ General Style Don't set things to undefined. Reserve that value to mean "not yet set to anything." Boolean objects are verboten. - Use JSDoc +- Use switch-case statements where there are 5 or more branches running against the same variable. ECMAScript ---------- From 2828cc1d258a70f48908969e044e735a9655645f Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 20 May 2020 15:13:31 +0000 Subject: [PATCH 210/399] Translated using Weblate (Albanian) Currently translated at 99.9% (2318 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 7058ad67b0..6519c54065 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2424,5 +2424,15 @@ "Click the button below to confirm setting up encryption.": "Klikoni mbi butonin më poshtë që të ripohoni ujdisjen e fshehtëzimit.", "Dismiss read marker and jump to bottom": "Mos merr parasysh piketë leximi dhe hidhu te fundi", "Jump to oldest unread message": "Hidhu te mesazhi më i vjetër i palexuar", - "Upload a file": "Ngarkoni një kartelë" + "Upload a file": "Ngarkoni një kartelë", + "Font scaling": "Përshkallëzim shkronjash", + "Use IRC layout": "Përdor skemë IRC-je", + "Font size": "Madhësi shkronjash", + "Custom font size": "Madhësi vetjake shkronjash", + "IRC display name width": "Gjerësi shfaqjeje emrash IRC", + "Size must be a number": "Madhësia duhet të jetë një numër", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Madhësia vetjake për shkronjat mund të jetë vetëm mes vlerave %(min)s pt dhe %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Përdor me %(min)s pt dhe %(max)s pt", + "Appearance": "Dukje", + "Create room": "Krijo dhomë" } From b1c80df0b4b7cf9d2fd4332435f5b3809cc81b48 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 21 May 2020 05:05:17 +0000 Subject: [PATCH 211/399] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2321 of 2321 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 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index f222d7d09f..7c55826005 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2437,5 +2437,12 @@ "Upload a file": "上傳檔案", "Use IRC layout": "使用 IRC 佈局", "IRC display name width": "IRC 顯示名稱寬度", - "Create room": "建立聊天室" + "Create room": "建立聊天室", + "Font scaling": "字型縮放", + "Font size": "字型大小", + "Custom font size": "自訂字型大小", + "Size must be a number": "大小必須為數字", + "Custom font size can only be between %(min)s pt and %(max)s pt": "自訂字型大小僅能為 %(min)s 點至 %(max)s 點間", + "Use between %(min)s pt and %(max)s pt": "使用 %(min)s 點至 %(max)s 點間", + "Appearance": "外觀" } From 19b9776e897d5de817aea1bb9d9374f846890af0 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Wed, 20 May 2020 15:09:40 +0000 Subject: [PATCH 212/399] Translated using Weblate (Esperanto) Currently translated at 100.0% (2321 of 2321 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 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 56b5d53008..89c75532df 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2412,5 +2412,12 @@ "Upload a file": "Alŝuti dosieron", "Create room": "Krei ĉambron", "Use IRC layout": "Uzi aranĝon de IRC", - "IRC display name width": "Larĝo de vidiga nomo de IRC" + "IRC display name width": "Larĝo de vidiga nomo de IRC", + "Font scaling": "Skalado de tiparoj", + "Font size": "Grando de tiparo", + "Custom font size": "Propra grando de tiparo", + "Size must be a number": "Grando devas esti nombro", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Propra grando de tiparo povas interi nur %(min)s punktojn kaj %(max)s punktojn", + "Use between %(min)s pt and %(max)s pt": "Uzi inter %(min)s punktoj kaj %(max)s punktoj", + "Appearance": "Aspekto" } From 4c39b6a9889f0a88ba0de44ee0065ec22da6d464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 21 May 2020 06:23:40 +0000 Subject: [PATCH 213/399] Translated using Weblate (French) Currently translated at 100.0% (2321 of 2321 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 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 7a89f71b73..ee3c398e86 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2438,5 +2438,12 @@ "Upload a file": "Envoyer un fichier", "Use IRC layout": "Utiliser la mise en page d’IRC", "IRC display name width": "Largeur du nom affiché IRC", - "Create room": "Créer un salon" + "Create room": "Créer un salon", + "Font scaling": "Mise à l’échelle de la police", + "Font size": "Taille de la police", + "Custom font size": "Taille personnalisée de la police", + "Size must be a number": "La taille doit être un nombre", + "Custom font size can only be between %(min)s pt and %(max)s pt": "La taille de police personnalisée doit être comprise entre %(min)s pt et %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Utiliser entre %(min)s pt et %(max)s pt", + "Appearance": "Apparence" } From 23bfa6f11ea192b8253d7f45bc00ae82a7124926 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 21 May 2020 08:05:57 +0000 Subject: [PATCH 214/399] Translated using Weblate (Italian) Currently translated at 100.0% (2321 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 359938ff3b..1fd6d7dc0c 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2433,5 +2433,12 @@ "Upload a file": "Invia un file", "Use IRC layout": "Usa il layout IRC", "IRC display name width": "Larghezza nome di IRC", - "Create room": "Crea stanza" + "Create room": "Crea stanza", + "Font scaling": "Ridimensionamento carattere", + "Font size": "Dimensione carattere", + "Custom font size": "Dimensione carattere personalizzata", + "Size must be a number": "La dimensione deve essere un numero", + "Custom font size can only be between %(min)s pt and %(max)s pt": "La dimensione del carattere personalizzata può solo essere tra %(min)s pt e %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Usa tra %(min)s pt e %(max)s pt", + "Appearance": "Aspetto" } From 0409932b4dee8293761f8aabb2d7d80d5e028e59 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Wed, 20 May 2020 14:30:14 +0000 Subject: [PATCH 215/399] Translated using Weblate (Lithuanian) Currently translated at 46.4% (1076 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index c893034644..fbdd41c16f 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -72,7 +72,7 @@ "Keywords": "Raktažodžiai", "Unpin Message": "Atsegti žinutę", "Enable notifications for this account": "Įjungti pranešimus šiai paskyrai", - "Remove": "Šalinti", + "Remove": "Pašalinti", "Invite to this community": "Pakviesti į šią bendruomenę", "Messages containing keywords": "Žinutės, kuriose yra raktažodžiai", "When I'm invited to a room": "Kai aš esu pakviestas į pokalbių kambarį", @@ -86,7 +86,7 @@ "Unnamed room": "Kambarys be pavadinimo", "Dismiss": "Atmesti", "Explore Account Data": "Peržiūrėti paskyros duomenis", - "Remove from Directory": "Šalinti iš katalogo", + "Remove from Directory": "Pašalinti iš katalogo", "Download this file": "Atsisiųsti šį failą", "Saturday": "Šeštadienis", "Remember, you can always set an email address in user settings if you change your mind.": "Nepamirškite, kad jei persigalvosite, tai bet kada galite nustatyti el. pašto adresą vartotojo nustatymuose.", @@ -146,7 +146,7 @@ "Off": "Išjungta", "Edit": "Koreguoti", "Mentions only": "Tik paminėjimai", - "remove %(name)s from the directory.": "šalinti %(name)s iš katalogo.", + "remove %(name)s from the directory.": "pašalinti %(name)s iš katalogo.", "You can now return to your account after signing out, and sign in on other devices.": "Po atsijungimo galite grįžti prie savo paskyros ir prisijungti kituose įrenginiuose.", "Continue": "Tęsti", "Enable email notifications": "Įjungti pranešimus el. paštu", @@ -353,8 +353,8 @@ "The phone number field must not be blank.": "Telefono numerio laukas negali būti tuščias.", "The password field must not be blank.": "Slaptažodžio laukas negali būti tuščias.", "Email address": "El. pašto adresas", - "Remove from community": "Šalinti iš bendruomenės", - "Remove this user from community?": "Šalinti šį naudotoją iš bendruomenės?", + "Remove from community": "Pašalinti iš bendruomenės", + "Remove this user from community?": "Pašalinti šį vartotoją iš bendruomenės?", "Failed to remove user from community": "Nepavyko pašalinti naudotoją iš bendruomenės", "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Ar tikrai norite pašalinti \"%(roomName)s\" iš %(groupId)s?", "Failed to remove room from community": "Nepavyko pašalinti kambarį iš bendruomenės", @@ -1018,8 +1018,8 @@ "Order rooms by name": "Rūšiuoti kambarius pagal pavadinimą", "The other party cancelled the verification.": "Kita šalis atšaukė patvirtinimą.", "Public Name": "Viešas Vardas", - "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Užšifruotos žinutės yra apsaugotos su \"end-to-end\" šifravimu. Tik jūs ir gavėjas(-ai) turi raktus šioms žinutėms perskaityti.", - "Back up your keys before signing out to avoid losing them.": "Prieš atsijungdami sukurkite atsarginę savo raktų kopiją, kad jų neprarastumėte.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Užšifruotos žinutės yra apsaugotos visapusiu šifravimu. Tik jūs ir gavėjas(-ai) turi raktus šioms žinutėms perskaityti.", + "Back up your keys before signing out to avoid losing them.": "Prieš atsijungdami sukurkite atsarginę savo raktų kopiją, kad išvengtumėte jų praradimo.", "Start using Key Backup": "Pradėti naudoti Atsarginę Raktų Kopiją", "Display Name": "Rodomas Vardas", "Please verify the room ID or alias and try again.": "Prašome patikrinti kambario ID arba slapyvardį ir bandyti dar kartą.", @@ -1093,5 +1093,24 @@ "Your recovery key": "Jūsų atgavimo raktas", "Copy": "Kopijuoti", "Make a copy of your recovery key": "Padaryti jūsų atgavimo rakto kopiją", - "Please enter your recovery passphrase a second time to confirm.": "Įveskite atgavimo slaptafrazę antrą kartą, kad patvirtintumėte." + "Please enter your recovery passphrase a second time to confirm.": "Įveskite atgavimo slaptafrazę antrą kartą, kad patvirtintumėte.", + "Later": "Vėliau", + "Verify yourself & others to keep your chats safe": "Patvirtinkite save ir kitus, kad jūsų pokalbiai būtų saugūs", + "Go back": "", + "This room is end-to-end encrypted": "Šis kambarys užšifruotas visapusiu šifravimu", + "Send a message…": "Siųsti žinutę…", + "Never lose encrypted messages": "Niekada nepraraskite šifruotų žinučių", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Žinutės šiame kambaryje yra apsaugotos visapusiu šifravimu. Tik jūs ir gavėjas(-ai) turite raktus šioms žinutėms perskaityti.", + "Securely back up your keys to avoid losing them. Learn more.": "Saugiai sukurkite jūsų raktų atsargines kopijas, kad išvengtumėte jų praradimo. Sužinoti daugiau.", + "Not now": "Ne dabar", + "Don't ask me again": "Daugiau neklausti", + "Send as message": "Siųsti kaip žinutę", + "Messages in this room are end-to-end encrypted.": "Žinutės šiame kambaryje yra užšifruotos visapusiu šifravimu.", + "Messages in this room are not end-to-end encrypted.": "Žinutės šiame kambaryje nėra užšifruotos visapusiu šifravimu.", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Žinutės šiame kambaryje yra užšifruotos visapusiu šifravimu. Sužinokite daugiau ir patvirtinkite šį vartotoją jų profilyje.", + "Confirm Removal": "Patvirtinkite pašalinimą", + "Manually export keys": "Eksportuoti raktus rankiniu būdu", + "Send a Direct Message": "Siųsti tiesioginę žinutę", + "Go Back": "", + "Go back to set it again.": "Grįžti atgal, kad nustatyti iš naujo." } From 3b353f1e7ab3cd7467e34cb55c1abf7c9a0d0721 Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Thu, 21 May 2020 09:21:10 +0000 Subject: [PATCH 216/399] Translated using Weblate (Slovak) Currently translated at 65.3% (1515 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index d117843ccf..4cd954c891 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -627,7 +627,7 @@ "Passphrase must not be empty": "Heslo nesmie byť prázdne", "Export room keys": "Exportovať kľúče miestností", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Tento proces vás prevedie exportom kľúčov určených na dešifrovanie správ, ktoré ste dostali v šifrovaných miestnostiach do lokálneho súboru. Tieto kľúče zo súboru môžete neskôr importovať do iného Matrix klienta, aby ste v ňom mohli dešifrovať vaše šifrované správy.", - "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Tento súbor umožní komukoľvek, k to má ku nemu prístup dešifrovať všetky vami viditeľné šifrované správy, mali by ste teda byť opatrní a tento súbor si bezpečne uchovať. Aby bolo toto pre vás jednoduchšie, nižšie zadajte heslo, ktorým budú údaje v súbore zašifrované. Importovať údaje zo súboru bude možné len po zadaní tohoto istého hesla.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Tento súbor umožní komukoľvek, kto má ku nemu prístup, dešifrovať všetky vami viditeľné šifrované správy, mali by ste teda byť opatrní a tento súbor si bezpečne uchovať. Aby bolo toto pre vás jednoduchšie, nižšie zadajte heslo, ktorým budú údaje v súbore zašifrované. Importovať údaje zo súboru bude možné len po zadaní tohoto istého hesla.", "Enter passphrase": "Zadajte (dlhé) heslo", "Confirm passphrase": "Potvrďte heslo", "Export": "Exportovať", @@ -1336,7 +1336,7 @@ "Create account": "Vytvoriť účet", "Registration has been disabled on this homeserver.": "Na tomto domovskom servery nie je povolená registrácia.", "Unable to query for supported registration methods.": "Nie je možné požiadať o podporované metódy registrácie.", - "Create your account": "Vytvoriť váš účet", + "Create your account": "Vytvorte si váš účet", "Keep going...": "Pokračujte…", "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Zašifrovanú kópiu vašich šifrovacích kľúčov uchováme na domovskom servery. Zabezpečte si zálohovanie zadaním hesla obnovenia, čo posilní ochranu vašich údajov.", "For maximum security, this should be different from your account password.": "Aby ste zachovali maximálnu mieru zabezpečenia, (dlhé) heslo by malo byť odlišné od hesla, ktorým sa prihlasujete do vášho účtu.", @@ -1408,7 +1408,7 @@ "Add Email Address": "Pridať emailovú adresu", "Add Phone Number": "Pridať telefónne číslo", "Send cross-signing keys to homeserver": "Poslať kľúče pre podpisovanie naprieč zariadeniami na domovský server", - "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Táto akcia si vyžaduje mať overenú emailovú adresu alebo telefónne číslo cez predvolený server totožností , ale server nezverejnil podmienky používania.", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Táto akcia vyžaduje prístup k predvolenému serveru totožností na overenie emailovej adresy alebo telefónneho čísla, ale server nemá žiadne podmienky používania.", "Trust": "Dôverovať", "Custom (%(level)s)": "Vlastný (%(level)s)", "Sends a message as plain text, without interpreting it as markdown": "Odošle správu vo formáte obyčajný text, bez prekladu markdown", @@ -1538,7 +1538,7 @@ "New login. Was this you?": "Nové pihlásenie. Ste to vy?", "%(name)s is requesting verification": "%(name) žiada o overenie", "Sign In or Create Account": "Prihlásiť sa alebo vytvoriť nový účet", - "Use your account or create a new one to continue.": "Použite váš existujúci účet alebo vytvorte si nový, aby ste mohli pokračovať.", + "Use your account or create a new one to continue.": "Použite váš existujúci účet alebo si vytvorte nový, aby ste mohli pokračovať.", "Create Account": "Vytvoriť účet", "Sign In": "Prihlásiť sa", "Sends a message as html, without interpreting it as markdown": "Pošlite správu ako HTML, bez interpretácie v Markdowne", @@ -1592,10 +1592,26 @@ "Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Zálohovací kľúč je uložený v bezpečnom úložisku, ale jeho načítanie nie je povolené v tejto relácií. Prosím, zapnite krížové podpisovanie v Experimentoch, aby ste mohli modifikovať stav zálohy.", "Cross-signing": "Krížové podpisovanie", "Destroy cross-signing keys?": "Zmazať kľúče pre krížové podpisovanie?", - "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Zmazanie kľúčov pre krížové podpisovanie je nenávratné. Každý, s kým ste sa overili, bude vidieť bezpečnostné upozornenia. Toto určite nechcete robiť dokiaľ ste nestratili všetky zariadenia, s ktorými by ste mohli krížovo podpisovať.", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Zmazanie kľúčov pre krížové podpisovanie je nenávratné. Každý, s kým ste sa overili, bude vidieť bezpečnostné upozornenia. Toto určite nechcete robiť, dokiaľ ste nestratili všetky zariadenia, s ktorými by ste mohli krížovo podpisovať.", "Clear cross-signing keys": "Zmazať kľúče pre krížové podpisovanie", "a new cross-signing key signature": "nový podpis kľúča pre krížové podpisovanie", "a device cross-signing signature": "podpis krížovo podpísaného zariadenia", "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Získajte prístup k vašej šifrovanej histórií správ a vašej krížom podpísanej identite pre overenie iných relácií zadaním vášho kľúču obnovy.", - "or another cross-signing capable Matrix client": "alebo iný Matrixový klient podporujúci krížové podpisovanie" + "or another cross-signing capable Matrix client": "alebo iný Matrixový klient podporujúci krížové podpisovanie", + "Removing…": "Odstraňovanie…", + "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Váš nový účet (%(newAccountId)s) je registrovaný, ale už ste prihlásený pod iným účtom (%(loggedInUserId)s).", + "Continue with previous account": "Pokračovať s predošlým účtom", + "Log in to your new account.": "Prihláste sa do vášho nového účtu.", + "You can now close this window or log in to your new account.": "Teraz môžete toto okno zavrieť alebo sa prihlásiť do vášho nového účtu.", + "Registration Successful": "Úspešná registrácia", + "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Potvrďte svoju identitu overením tohto účtu z jednej z vašich iných relácií, čím mu povolíte prístup k šifrovaným správam.", + "This requires the latest Riot on your other devices:": "Toto vyžaduje najnovší Riot na vašich ostatných zariadeniach:", + "Use Recovery Passphrase or Key": "Použite (dlhé) heslo pre obnovu zálohy alebo kľúč", + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Vaša nová relácia je teraz overená. Má prístup k vašim šifrovaným správam a ostatný používatelia ju uvidia ako dôveryhodnú.", + "Your new session is now verified. Other users will see it as trusted.": "Vaša nová relácia je teraz overená. Ostatný používatelia ju uvidia ako dôveryhodnú.", + "Without completing security on this session, it won’t have access to encrypted messages.": "Bez dokončenia overenia nebude mať táto relácia prístup k šifrovaným správam.", + "Go Back": "Späť", + "Failed to re-authenticate due to a homeserver problem": "Opätovná autentifikácia zlyhala kvôli problému domovského servera", + "Failed to re-authenticate": "Opätovná autentifikácia zlyhala", + "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Znovuzískajte prístup k vášmu účtu a obnovte šifrovacie kľúče uložené v tejto relácií. Bez nich nebudete môcť čítať všetky vaše šifrované správy vo všetkých reláciach." } From 5d1c01fd6fd006443bbf4d6f1e0e731a99ad042e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 21 May 2020 12:26:27 +0100 Subject: [PATCH 217/399] Fix key backup restore with SSSS The room / session ID params come after the backupInfo for restoring from SSSS so the options object was being passed into the wrong param. Roll on TypeScript. This meant restoring backups worked fine when the key was cached but failed when it wasn't. Regressed in https://github.com/matrix-org/matrix-react-sdk/pull/4507 --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 7e51e76f6c..a16202ed93 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -201,7 +201,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { // `accessSecretStorage` may prompt for storage access as needed. const recoverInfo = await accessSecretStorage(async () => { return MatrixClientPeg.get().restoreKeyBackupWithSecretStorage( - this.state.backupInfo, + this.state.backupInfo, undefined, undefined, { progressCallback: this._progressCallback }, ); }); From 53d90234c35cf232cd527011abeee5f1ff8a9cd8 Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Thu, 21 May 2020 10:53:55 +0000 Subject: [PATCH 218/399] Translated using Weblate (Czech) Currently translated at 94.7% (2198 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/cs/ --- src/i18n/strings/cs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index ddd5e5521b..e5568d7bfb 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -2150,7 +2150,7 @@ "Sign In or Create Account": "Přihlásit nebo vytvořit nový účet", "Use your account or create a new one to continue.": "Pro pokračování se přihlaste existujícím účtem, nebo si vytvořte nový.", "Create Account": "Vytvořit účet", - "Order rooms by name": "Seřadit místnosti podle názvz", + "Order rooms by name": "Seřadit místnosti podle názvu", "Show rooms with unread notifications first": "Zobrazovat místnosti s nepřečtenými oznámeními navrchu", "Show shortcuts to recently viewed rooms above the room list": "Zobrazovat zkratky do nedávno zobrazených místností navrchu", "Cancelling…": "Rušení…", From a3e790d72ca58f4220715821c73be35cf1b20bb5 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Thu, 21 May 2020 11:19:30 +0000 Subject: [PATCH 219/399] Translated using Weblate (Lithuanian) Currently translated at 47.0% (1090 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index fbdd41c16f..54ebd93051 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -65,7 +65,7 @@ "Cancel": "Atšaukti", "Filter results": "Išfiltruoti rezultatus", "Members": "Nariai", - "No update available.": "Nėra prieinamų atnaujinimų.", + "No update available.": "Nėra galimų atnaujinimų.", "Noisy": "Triukšmingas", "Collecting app version information": "Renkama programėlės versijos informacija", "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Ar ištrinti kambarį %(alias)s ir %(name)s kambario pavadinimą iš katalogo?", @@ -705,7 +705,7 @@ "That doesn't look like a valid email address": "Tai nepanašu į teisingą el. pašto adresą", "Preparing to send logs": "Ruošiamasi išsiųsti žurnalus", "Incompatible Database": "Nesuderinama duomenų bazė", - "Deactivate Account": "Pasyvinti paskyrą", + "Deactivate Account": "Deaktyvuoti paskyrą", "I verify that the keys match": "Aš patvirtinu, kad raktai sutampa", "Incompatible local cache": "Nesuderinamas vietinis podėlis", "Updating Riot": "Atnaujinama Riot", @@ -1112,5 +1112,19 @@ "Manually export keys": "Eksportuoti raktus rankiniu būdu", "Send a Direct Message": "Siųsti tiesioginę žinutę", "Go Back": "", - "Go back to set it again.": "Grįžti atgal, kad nustatyti iš naujo." + "Go back to set it again.": "Grįžti atgal, kad nustatyti iš naujo.", + "Click the button below to confirm adding this email address.": "Paspauskite mygtuką žemiau, kad patvirtintumėte šio el. pašto pridėjimą.", + "Add an email address to configure email notifications": "Pridėkite el. pašto adresą, kad nustatytumėte el. pašto pranešimus", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Prieš atsijungiant rekomenduojame iš identiteto serverio pašalinti savo el. pašto adresus ir telefono numerius.", + "Email addresses": "El. pašto adresai", + "Account management": "Paskyros valdymas", + "Deactivating your account is a permanent action - be careful!": "Paskyros deaktyvavimas yra neatšaukiamas veiksmas - būkite atsargūs!", + "Your email address hasn't been verified yet": "Jūsų el. pašto adresas dar nebuvo patvirtintas", + "We've sent you an email to verify your address. Please follow the instructions there and then click the button below.": "Išsiuntėme jums el. laišką, kad patvirtintumėme jūsų adresą. Sekite ten esančiais nurodymais ir tada paspauskite žemiau esantį mygtuką.", + "Email Address": "El. pašto adresas", + "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "Jūs galite naudoti pasirinktinius serverio nustatymus, kad prisijungtumėte prie kitų Matrix serverių, nurodydami kito serverio URL. Tai leidžia jums naudotis šia programa su kitame serveryje esančia Matrix paskyra.", + "Enter your custom homeserver URL What does this mean?": "Įveskite pasirinktinio serverio URL Ką tai reiškia?", + "Homeserver URL": "Serverio URL", + "Homeserver URL does not appear to be a valid Matrix homeserver": "Serverio adresas neatrodo esantis tinkamas Matrix serveris", + "This homeserver does not support login using email address.": "Šis serveris nepalaiko prisijungimo naudojant el. pašto adresą." } From 6d92f54934e8b7a4947bf71a3e14a754bac91bf6 Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Thu, 21 May 2020 10:07:36 +0000 Subject: [PATCH 220/399] Translated using Weblate (Slovak) Currently translated at 66.7% (1547 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 51 ++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 4cd954c891..a9111a91a6 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -540,7 +540,7 @@ "Show timestamps in 12 hour format (e.g. 2:30pm)": "Pri zobrazovaní časových značiek používať 12 hodinový formát (napr. 2:30pm)", "Use compact timeline layout": "Použiť kompaktné rozloženie časovej osy", "Enable automatic language detection for syntax highlighting": "Povoliť automatickú detegciu jazyka pre zvýrazňovanie syntaxe", - "Automatically replace plain text Emoji": "Automaticky nahrádzať textové Emoji", + "Automatically replace plain text Emoji": "Automaticky nahrádzať textové emotikony modernými emoji", "Mirror local video feed": "Zrkadliť lokálne video", "Light theme": "Svetlý vzhľad", "Dark theme": "Tmavý vzhľad", @@ -604,7 +604,7 @@ "Stops ignoring a user, showing their messages going forward": "Prestane ignorovať používateľa a začne zobrazovať jeho správy", "Commands": "Príkazy", "Results from DuckDuckGo": "Výsledky z DuckDuckGo", - "Emoji": "Emoji", + "Emoji": "Emotikon", "Notify the whole room": "Oznamovať celú miestnosť", "Room Notification": "Oznámenie miestnosti", "Users": "Používatelia", @@ -1089,14 +1089,14 @@ "The user must be unbanned before they can be invited.": "Tomuto používateľovi musíte pred odoslaním pozvania povoliť vstup.", "Group & filter rooms by custom tags (refresh to apply changes)": "Zoskupiť a filtrovať miestnosti podľa vlastných značiek (zmeny sa prejavia po obnovení stránky)", "Render simple counters in room header": "Zobraziť jednoduchú štatistiku v záhlaví miestnosti", - "Enable Emoji suggestions while typing": "Umožniť automatické návrhy Emoji počas písania", + "Enable Emoji suggestions while typing": "Umožniť automatické návrhy emoji počas písania", "Show a placeholder for removed messages": "Zobrazovať náhrady za odstránené správy", "Show join/leave messages (invites/kicks/bans unaffected)": "Zobrazovať správy o vstupe a opustení miestnosti (Nemá vplyv na pozvania/vykázania/zákazy vstupu)", "Show avatar changes": "Zobrazovať zmeny obrázka v profile", "Show display name changes": "Zobrazovať zmeny zobrazovaného mena", "Show read receipts sent by other users": "Zobrazovať potvrdenia o prečítaní od ostatných používateľov", "Show avatars in user and room mentions": "Pri zmienkach používateľov a miestností zobrazovať aj obrázok", - "Enable big emoji in chat": "Povoliť veľké Emoji v konverzáciách", + "Enable big emoji in chat": "Povoliť veľké emoji v konverzáciách", "Send typing notifications": "Posielať oznámenia, keď píšete", "Enable Community Filter Panel": "Povoliť panel filter komunít", "Allow Peer-to-Peer for 1:1 calls": "Povoliť P2P počas priamych audio/video hovorov", @@ -1107,7 +1107,7 @@ "You've successfully verified this user.": "Úspešne ste overili tohoto používateľa.", "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Zabezpečené správi s týmto používateľom sú E2E šifrované, čo znamená, že čítanie tretími stranami nie je možné.", "Got It": "Rozumiem", - "Verify this user by confirming the following emoji appear on their screen.": "Overte tohoto používateľa tým, že zistíte, či sa na jeho obrazovke objaví nasledujúci emoji.", + "Verify this user by confirming the following emoji appear on their screen.": "Overte tohto používateľa tak, že zistíte, či sa na jeho obrazovke objaví nasledujúci emoji.", "Verify this user by confirming the following number appears on their screen.": "Overte tohoto používateľa tým, že zistíte, či sa na jeho obrazovke objaví nasledujúce číslo.", "Unable to find a supported verification method.": "Nie je možné nájsť podporovanú metódu overenia.", "Dog": "Hlava psa", @@ -1573,7 +1573,7 @@ "Ask this user to verify their session, or manually verify it below.": "Poproste tohto používateľa, aby si overil svoju reláciu alebo ju nižšie manuálne overte.", "Not Trusted": "Nedôveryhodné", "Manually Verify by Text": "Manuálne overte pomocou textu", - "Interactively verify by Emoji": "Interaktívne overte pomocou emotikonov", + "Interactively verify by Emoji": "Interaktívne overte pomocou emoji", "Done": "Hotovo", "a few seconds ago": "pred pár sekundami", "about a minute ago": "približne pred minutou", @@ -1613,5 +1613,42 @@ "Go Back": "Späť", "Failed to re-authenticate due to a homeserver problem": "Opätovná autentifikácia zlyhala kvôli problému domovského servera", "Failed to re-authenticate": "Opätovná autentifikácia zlyhala", - "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Znovuzískajte prístup k vášmu účtu a obnovte šifrovacie kľúče uložené v tejto relácií. Bez nich nebudete môcť čítať všetky vaše šifrované správy vo všetkých reláciach." + "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Znovuzískajte prístup k vášmu účtu a obnovte šifrovacie kľúče uložené v tejto relácií. Bez nich nebudete môcť čítať všetky vaše šifrované správy vo všetkých reláciach.", + "Font scaling": "Škálovanie písma", + "Use IRC layout": "Použiť IRC rozloženie", + "Show info about bridges in room settings": "Zobraziť informácie o mostoch v Nastaveniach miestnosti", + "Font size": "Veľkosť písma", + "Custom font size": "Vlastná veľkosť písma", + "Show typing notifications": "Posielať oznámenia, keď píšete", + "Never send encrypted messages to unverified sessions from this session": "Nikdy neposielať šifrované správy neovereným reláciam z tejto relácie", + "Never send encrypted messages to unverified sessions in this room from this session": "Nikdy neposielať šifrované správy neovereným reláciam v tejto miestnosti z tejto relácie", + "Order rooms by name": "Zoradiť miestnosti podľa názvu", + "Show rooms with unread notifications first": "Zobraziť miestnosti s neprečítanými oznámeniami navrchu", + "Show shortcuts to recently viewed rooms above the room list": "Zobraziť skratky nedávno zobrazených miestnosti nad zoznamom miestností", + "Enable message search in encrypted rooms": "Povoliť vyhľadávanie správ v šifrovaných miestnostiach", + "How fast should messages be downloaded.": "Ako rýchlo sa majú správy sťahovať.", + "Manually verify all remote sessions": "Manuálne overiť všetky relácie", + "IRC display name width": "Šírka zobrazovaného mena IRC", + "Verify this session by completing one of the following:": "Overte túto reláciu dokončením jedného z nasledujúcich:", + "Scan this unique code": "Naskenujte tento jedinečný kód", + "or": "alebo", + "Compare unique emoji": "Porovnajte jedinečnú kombináciu emoji", + "Compare a unique set of emoji if you don't have a camera on either device": "Pokiaľ nemáte na svojich zariadeniach kameru, porovnajte jedinečnú kombináciu emoji", + "Confirm the emoji below are displayed on both sessions, in the same order:": "Potvrďte, že nasledujúce emoji sú zobrazené na oboch reláciach v rovnakom poradí:", + "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "Relácia, ktorú sa snažíte overiť, nepodporuje overovanie QR kódom a ani pomocou emoji, čo sú funkcie, ktoré Riot podporuje. Skúste použiť iného klienta.", + "QR Code": "QR kód", + "Enter your password to sign in and regain access to your account.": "Prihláste sa zadaním hesla a znovuzískajte prístup k vášmu účtu.", + "Forgotten your password?": "Zabudli ste heslo?", + "Sign in and regain access to your account.": "Prihláste sa a znovuzískajte prístup k vášmu účtu.", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Nemôžete sa prihlásiť do vášho účtu. Kontaktujte prosím vášho správcu domovského servera pre viac informácií.", + "You're signed out": "Ste odhlásený", + "Clear personal data": "Zmazať osobné dáta", + "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Varovanie: Vaše osobné údaje (vrátane šifrovacích kľúčov) sú stále uložené v tejto relácií. Zmažte ich, ak chcete túto reláciu zahodiť alebo sa chcete prihlásiť cez iný účet.", + "Command Autocomplete": "Automatické dopĺňanie príkazov", + "Community Autocomplete": "Automatické dopĺňanie skupín", + "DuckDuckGo Results": "Výsledky hľadania DuckDuckGo", + "Emoji Autocomplete": "Automatické dopĺňanie emotikonov", + "Notification Autocomplete": "Automatické dopĺňanie oznámení", + "Room Autocomplete": "Automatické dopĺňanie miestností", + "User Autocomplete": "Automatické dopĺňanie používateľov" } From 7e7c48e0460ae163b2d3a66a1b54f767f8777e71 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 16:32:19 +0100 Subject: [PATCH 221/399] Improve style of toasts to match Figma Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/structures/_ToastContainer.scss | 46 ++++++++++++++++----- src/components/structures/ToastContainer.js | 5 ++- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index 6ec4a0d152..d39d6cdfe8 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -28,8 +28,8 @@ limitations under the License. margin: 0 4px; grid-row: 2 / 4; grid-column: 1; - background-color: white; - box-shadow: 0px 4px 12px $menu-box-shadow-color; + background-color: $dark-panel-bg-color; + box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.5); border-radius: 8px; } @@ -37,8 +37,8 @@ limitations under the License. grid-row: 1 / 3; grid-column: 1; color: $primary-fg-color; - background-color: $primary-bg-color; - box-shadow: 0px 4px 12px $menu-box-shadow-color; + background-color: $dark-panel-bg-color; + box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.5); border-radius: 8px; overflow: hidden; display: grid; @@ -73,12 +73,23 @@ limitations under the License. } } - h2 { - grid-column: 1 / 3; - grid-row: 1; - margin: 0; - font-size: $font-15px; - font-weight: 600; + .mx_Toast_title { + h2 { + grid-column: 1 / 3; + grid-row: 1; + margin: 0; + font-size: $font-15px; + font-weight: 600; + display: inline; + width: auto; + } + + span { + float: right; + font-size: $font-12px; + line-height: $font-21px; + color: $muted-fg-color; + } } .mx_Toast_body { @@ -87,7 +98,13 @@ limitations under the License. } .mx_Toast_buttons { + float: right; display: flex; + + .mx_FormButton { + min-width: 96px; + box-sizing: border-box; + } } .mx_Toast_description { @@ -96,6 +113,15 @@ limitations under the License. text-overflow: ellipsis; margin: 4px 0 11px 0; font-size: $font-12px; + + .mx_AccessibleButton_kind_link { + font-size: inherit; + padding: 0; + } + + a { + text-decoration: none; + } } .mx_Toast_deviceID { diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.js index 283fbdd96a..ddbef42421 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.js @@ -57,7 +57,10 @@ export default class ToastContainer extends React.Component { toastKey: key, }); toast = (
    -

    {title}{countIndicator}

    +
    +

    {title}

    + {countIndicator} +
    {React.createElement(component, toastProps)}
    ); } From 5995a27ced87200c60b208a5549e6b4a6c608831 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 16:52:36 +0100 Subject: [PATCH 222/399] Iterate paddings Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/structures/_ToastContainer.scss | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index d39d6cdfe8..464eaf488d 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -46,7 +46,6 @@ limitations under the License. column-gap: 10px; row-gap: 4px; padding: 8px; - padding-right: 16px; &.mx_Toast_hasIcon { &::after { @@ -68,12 +67,27 @@ limitations under the License. background-image: url("$(res)/img/e2e/warning.svg"); } - h2, .mx_Toast_body { + .mx_Toast_title, .mx_Toast_body { grid-column: 2; } } + &:not(.mx_Toast_hasIcon) { + padding-left: 12px; + + .mx_Toast_title { + grid-column: 1 / -1; + } + } + + .mx_Toast_title, + .mx_Toast_description { + padding-right: 8px; + } .mx_Toast_title { + width: 100%; + box-sizing: border-box; + h2 { grid-column: 1 / 3; grid-row: 1; From 6a0ffe905f14ec916ab32ed10601fcb63f912ecb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 17:12:16 +0100 Subject: [PATCH 223/399] Iterate text alignment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/structures/_ToastContainer.scss | 3 ++- src/components/structures/ToastContainer.js | 6 +++++- src/i18n/strings/en_EN.json | 1 - 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index 464eaf488d..f64e688498 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -99,9 +99,10 @@ limitations under the License. } span { + padding-left: 8px; float: right; font-size: $font-12px; - line-height: $font-21px; + line-height: $font-22px; color: $muted-fg-color; } } diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.js index ddbef42421..729171bd6a 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.js @@ -50,7 +50,11 @@ export default class ToastContainer extends React.Component { "mx_Toast_hasIcon": icon, [`mx_Toast_icon_${icon}`]: icon, }); - const countIndicator = isStacked ? _t(" (1/%(totalCount)s)", {totalCount}) : null; + + let countIndicator; + if (isStacked) { + countIndicator = `1/${totalCount}`; + } const toastProps = Object.assign({}, props, { key, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cd09170285..99b1d2ad6f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2073,7 +2073,6 @@ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "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", - " (1/%(totalCount)s)": " (1/%(totalCount)s)", "Guest": "Guest", "Your profile": "Your profile", "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", From 1dfd62c1425c9f6462ccc424d475e70461e1b4f1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 17:47:35 +0100 Subject: [PATCH 224/399] iterate alignment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/structures/_ToastContainer.scss | 5 +++-- src/components/structures/ToastContainer.js | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index f64e688498..2916c4ffdc 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -42,8 +42,8 @@ limitations under the License. border-radius: 8px; overflow: hidden; display: grid; - grid-template-columns: 20px 1fr; - column-gap: 10px; + grid-template-columns: 22px 1fr; + column-gap: 8px; row-gap: 4px; padding: 8px; @@ -96,6 +96,7 @@ limitations under the License. font-weight: 600; display: inline; width: auto; + vertical-align: middle; } span { diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.js index 729171bd6a..8f20dcd61d 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.js @@ -15,7 +15,6 @@ limitations under the License. */ import * as React from "react"; -import { _t } from '../../languageHandler'; import ToastStore from "../../stores/ToastStore"; import classNames from "classnames"; From f539a960e1af71b1fec4e2bedd7fe3a2d240f951 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 17:50:32 +0100 Subject: [PATCH 225/399] re-add brackets Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/ToastContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.js index 8f20dcd61d..c7789d861f 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.js @@ -52,7 +52,7 @@ export default class ToastContainer extends React.Component { let countIndicator; if (isStacked) { - countIndicator = `1/${totalCount}`; + countIndicator = `(1/${totalCount})`; } const toastProps = Object.assign({}, props, { From b27f1fa6db112bb6da4bc3e562fb4782fb7a0f88 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 18:06:36 +0100 Subject: [PATCH 226/399] Convert BasePlatform and BaseEventIndexManager to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/{BasePlatform.js => BasePlatform.ts} | 55 ++++++++----------- ...dexManager.js => BaseEventIndexManager.ts} | 12 ++-- 2 files changed, 28 insertions(+), 39 deletions(-) rename src/{BasePlatform.js => BasePlatform.ts} (79%) rename src/indexing/{BaseEventIndexManager.js => BaseEventIndexManager.ts} (97%) diff --git a/src/BasePlatform.js b/src/BasePlatform.ts similarity index 79% rename from src/BasePlatform.js rename to src/BasePlatform.ts index e3cbc4dcf0..3f11f25d62 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.ts @@ -1,5 +1,3 @@ -// @flow - /* Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd @@ -19,9 +17,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClient} from "matrix-js-sdk"; +import {MatrixClient} from "matrix-js-sdk/src/client"; import dis from './dispatcher/dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; +import {ActionPayload} from "./dispatcher/payloads"; /** * Base class for classes that provide platform-specific functionality @@ -29,27 +28,25 @@ import BaseEventIndexManager from './indexing/BaseEventIndexManager'; * * Instances of this class are provided by the application. */ -export default class BasePlatform { - constructor() { - this.notificationCount = 0; - this.errorDidOccur = false; +export default abstract class BasePlatform { + protected notificationCount: number = 0; + protected errorDidOccur: boolean = false; - dis.register(this._onAction.bind(this)); + constructor() { + dis.register(this.onAction); } - _onAction(payload: Object) { + protected onAction = (payload: ActionPayload) => { switch (payload.action) { case 'on_client_not_viable': case 'on_logged_out': this.setNotificationCount(0); break; } - } + }; // Used primarily for Analytics - getHumanReadableName(): string { - return 'Base Platform'; - } + abstract getHumanReadableName(): string; setNotificationCount(count: number) { this.notificationCount = count; @@ -84,22 +81,16 @@ export default class BasePlatform { * that is 'granted' if the user allowed the request or * 'denied' otherwise. */ - requestNotificationPermission(): Promise { - } + abstract requestNotificationPermission(): Promise; - displayNotification(title: string, msg: string, avatarUrl: string, room: Object) { - } + abstract displayNotification(title: string, msg: string, avatarUrl: string, room: Object); - loudNotification(ev: Event, room: Object) { - } + abstract loudNotification(ev: Event, room: Object); /** - * Returns a promise that resolves to a string representing - * the current version of the application. + * Returns a promise that resolves to a string representing the current version of the application. */ - getAppVersion(): Promise { - throw new Error("getAppVersion not implemented!"); - } + abstract getAppVersion(): Promise; /* * If it's not expected that capturing the screen will work @@ -114,20 +105,18 @@ export default class BasePlatform { * Restarts the application, without neccessarily reloading * any application code */ - reload() { - throw new Error("reload not implemented!"); - } + abstract reload(); supportsAutoLaunch(): boolean { return false; } // XXX: Surely this should be a setting like any other? - async getAutoLaunchEnabled(): boolean { + async getAutoLaunchEnabled(): Promise { return false; } - async setAutoLaunchEnabled(enabled: boolean): void { + async setAutoLaunchEnabled(enabled: boolean): Promise { throw new Error("Unimplemented"); } @@ -135,11 +124,11 @@ export default class BasePlatform { return false; } - async getAutoHideMenuBarEnabled(): boolean { + async getAutoHideMenuBarEnabled(): Promise { return false; } - async setAutoHideMenuBarEnabled(enabled: boolean): void { + async setAutoHideMenuBarEnabled(enabled: boolean): Promise { throw new Error("Unimplemented"); } @@ -147,11 +136,11 @@ export default class BasePlatform { return false; } - async getMinimizeToTrayEnabled(): boolean { + async getMinimizeToTrayEnabled(): Promise { return false; } - async setMinimizeToTrayEnabled(enabled: boolean): void { + async setMinimizeToTrayEnabled(enabled: boolean): Promise { throw new Error("Unimplemented"); } diff --git a/src/indexing/BaseEventIndexManager.js b/src/indexing/BaseEventIndexManager.ts similarity index 97% rename from src/indexing/BaseEventIndexManager.js rename to src/indexing/BaseEventIndexManager.ts index f780c8e9ce..c40d1300ea 100644 --- a/src/indexing/BaseEventIndexManager.js +++ b/src/indexing/BaseEventIndexManager.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -20,7 +20,7 @@ export interface MatrixEvent { content: {}; event_id: string; origin_server_ts: number; - unsigned: ?{}; + unsigned?: {}; room_id: string; } @@ -59,7 +59,7 @@ export interface SearchArgs { before_limit: number; after_limit: number; order_by_recency: boolean; - room_id: ?string; + room_id?: string; } export interface EventAndProfile { @@ -85,7 +85,7 @@ export interface IndexStats { * * Instances of this class are provided by the application. */ -export default class BaseEventIndexManager { +export default abstract class BaseEventIndexManager { /** * Does our EventIndexManager support event indexing. * @@ -119,7 +119,7 @@ export default class BaseEventIndexManager { * @return {Promise} A promise that will resolve when the was queued up for * addition. */ - async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise<> { + async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise { throw new Error("Unimplemented"); } @@ -188,7 +188,7 @@ export default class BaseEventIndexManager { events: [EventAndProfile], checkpoint: CrawlerCheckpoint | null, oldCheckpoint: CrawlerCheckpoint | null, - ): Promise { + ): Promise { throw new Error("Unimplemented"); } From 7b0e51a703ea8c4aa7420577dad0c1f9dbf11265 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 18:11:21 +0100 Subject: [PATCH 227/399] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 3f11f25d62..f7e32584c3 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -29,8 +29,8 @@ import {ActionPayload} from "./dispatcher/payloads"; * Instances of this class are provided by the application. */ export default abstract class BasePlatform { - protected notificationCount: number = 0; - protected errorDidOccur: boolean = false; + protected notificationCount = 0; + protected errorDidOccur = false; constructor() { dis.register(this.onAction); From 3ae38714c10de32f0064550e067401566bc3c728 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 21 May 2020 18:25:52 +0100 Subject: [PATCH 228/399] make ts happy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index f7e32584c3..d4a6c34daf 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -85,7 +85,8 @@ export default abstract class BasePlatform { abstract displayNotification(title: string, msg: string, avatarUrl: string, room: Object); - abstract loudNotification(ev: Event, room: Object); + loudNotification(ev: Event, room: Object) { + }; /** * Returns a promise that resolves to a string representing the current version of the application. From 559dd98d01c0192f9a3d5c7ee56d77b9324d0ac6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 21 May 2020 11:53:16 -0600 Subject: [PATCH 229/399] Fix comment style to be less bothersome --- src/components/views/rooms/RoomList2.tsx | 3 +-- src/components/views/rooms/RoomSublist2.tsx | 3 +-- src/components/views/rooms/RoomTile2.tsx | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index a5d0175f01..d0c147c953 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -35,8 +35,7 @@ import { ActionPayload } from "../../../dispatcher/payloads"; * This is a work in progress implementation and isn't complete or * * even useful as a component. Please avoid using it until this * * warning disappears. * - ******************************************************************* - */ + *******************************************************************/ interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 4c3f65b323..e2f489b959 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -35,8 +35,7 @@ import RoomTile2 from "./RoomTile2"; * This is a work in progress implementation and isn't complete or * * even useful as a component. Please avoid using it until this * * warning disappears. * - ******************************************************************* - */ + *******************************************************************/ interface IProps { forRooms: boolean; diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 53e56d7a1e..42b65cba87 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -37,8 +37,7 @@ import * as FormattingUtils from "../../../utils/FormattingUtils"; * This is a work in progress implementation and isn't complete or * * even useful as a component. Please avoid using it until this * * warning disappears. * - ******************************************************************* - */ + *******************************************************************/ interface IProps { room: Room; From a11985f239831290c4da4507c46535ea74f06838 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 21 May 2020 11:54:38 -0600 Subject: [PATCH 230/399] Which component? The room list! --- src/stores/room-list/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/room-list/README.md b/src/stores/room-list/README.md index 020108878b..82a6e841db 100644 --- a/src/stores/room-list/README.md +++ b/src/stores/room-list/README.md @@ -106,7 +106,7 @@ put the sticky room in a position where it's had to decrease N will not increase ## Responsibilities of the store -The store is responsible for the ordering, upkeep, and tracking of all rooms. The component simply gets +The store is responsible for the ordering, upkeep, and tracking of all rooms. The room list component simply gets an object containing the tags it needs to worry about and the rooms within. The room list component will decide which tags need rendering (as it commonly filters out empty tags in most cases), and will deal with all kinds of filtering. From 3741c296aa3147bb3d9ed4f1b348118d4b98b3b6 Mon Sep 17 00:00:00 2001 From: XoseM Date: Thu, 21 May 2020 14:40:28 +0000 Subject: [PATCH 231/399] Translated using Weblate (Galician) Currently translated at 42.0% (974 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index f3b9e38273..d433d58146 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -962,5 +962,29 @@ "Enter your password to sign in and regain access to your account.": "Escribe o contrasinal para conectarte e retomar o acceso a túa conta.", "Sign in and regain access to your account.": "Conéctate e recupera o acceso a túa conta.", "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Non podes conectar a conta. Contacta coa administración do teu servidor para máis información.", - "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Aviso: os teus datos personais (incluíndo chaves de cifrado) aínda están gardadas nesta sesión. Pechaa se remataches de usar esta sesión, ou se quere conectar con outra conta." + "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Aviso: os teus datos personais (incluíndo chaves de cifrado) aínda están gardadas nesta sesión. Pechaa se remataches de usar esta sesión, ou se quere conectar con outra conta.", + "Unable to load! Check your network connectivity and try again.": "Non cargou! Comproba a conexión a rede e volta a intentalo.", + "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Hai sesións descoñecidas nesta sala: se continúas sen verificalas será posible para alguén fisgar na túa chamada.", + "Review Sessions": "Revisar Sesións", + "Call failed due to misconfigured server": "Fallou a chamada porque o servidor está mal configurado", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Contacta coa administración do teu servidor (%(homeserverDomain)s) para configurar un servidor TURN para que as chamadas funcionen de xeito fiable.", + "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "De xeito alternativo, podes intentar usar o servidor público turn.matrix.org, pero non é tan fiable, e compartirá o teu enderezo IP con ese servidor. Podes xestionar esto en Axustes.", + "Try using turn.matrix.org": "Inténtao usando turn.matrix.org", + "Replying With Files": "Respondendo con Ficheiros", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste intre non é posible responder cun ficheiro. Queres subir este ficheiro sen responder?", + "The file '%(fileName)s' failed to upload.": "Fallou a subida do ficheiro '%(fileName)s'.", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "O ficheiro '%(fileName)s' supera o tamaño máximo permitido polo servidor", + "The server does not support the room version specified.": "O servidor non soporta a versión da sala indicada.", + "If you cancel now, you won't complete verifying the other user.": "Se cancelas agora non completarás a verificación da outra usuaria.", + "If you cancel now, you won't complete verifying your other session.": "Se cancelas agora non completarás o proceso de verificación da outra sesión.", + "If you cancel now, you won't complete your operation.": "Se cancelas agora, non completarás a operación.", + "Cancel entering passphrase?": "Cancelar a escrita da frase de paso?", + "Setting up keys": "Configurando as chaves", + "Verify this session": "Verificar esta sesión", + "Encryption upgrade available": "Mellora do cifrado dispoñible", + "Set up encryption": "Configurar cifrado", + "Review where you’re logged in": "Revisar onde estás conectada", + "New login. Was this you?": "Nova conexión. Foches ti?", + "Name or Matrix ID": "Nome ou ID Matrix", + "Identity server has no terms of service": "O servidor de identidade non ten termos dos servizo" } From e849d288741765950e63fb532e1117e2aa9facd8 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Thu, 21 May 2020 13:42:07 +0000 Subject: [PATCH 232/399] Translated using Weblate (Lithuanian) Currently translated at 48.2% (1119 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 54ebd93051..e96e0e9199 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1092,11 +1092,11 @@ "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Laikykite šio rakto kopiją saugioje vietoje, pavyzdžiui slaptažodžių tvarkyklėje arba seife.", "Your recovery key": "Jūsų atgavimo raktas", "Copy": "Kopijuoti", - "Make a copy of your recovery key": "Padaryti jūsų atgavimo rakto kopiją", + "Make a copy of your recovery key": "Padaryti atgavimo rakto kopiją", "Please enter your recovery passphrase a second time to confirm.": "Įveskite atgavimo slaptafrazę antrą kartą, kad patvirtintumėte.", "Later": "Vėliau", "Verify yourself & others to keep your chats safe": "Patvirtinkite save ir kitus, kad jūsų pokalbiai būtų saugūs", - "Go back": "", + "Go back": "Grįžti", "This room is end-to-end encrypted": "Šis kambarys užšifruotas visapusiu šifravimu", "Send a message…": "Siųsti žinutę…", "Never lose encrypted messages": "Niekada nepraraskite šifruotų žinučių", @@ -1111,7 +1111,7 @@ "Confirm Removal": "Patvirtinkite pašalinimą", "Manually export keys": "Eksportuoti raktus rankiniu būdu", "Send a Direct Message": "Siųsti tiesioginę žinutę", - "Go Back": "", + "Go Back": "Grįžti", "Go back to set it again.": "Grįžti atgal, kad nustatyti iš naujo.", "Click the button below to confirm adding this email address.": "Paspauskite mygtuką žemiau, kad patvirtintumėte šio el. pašto pridėjimą.", "Add an email address to configure email notifications": "Pridėkite el. pašto adresą, kad nustatytumėte el. pašto pranešimus", @@ -1126,5 +1126,32 @@ "Enter your custom homeserver URL What does this mean?": "Įveskite pasirinktinio serverio URL Ką tai reiškia?", "Homeserver URL": "Serverio URL", "Homeserver URL does not appear to be a valid Matrix homeserver": "Serverio adresas neatrodo esantis tinkamas Matrix serveris", - "This homeserver does not support login using email address.": "Šis serveris nepalaiko prisijungimo naudojant el. pašto adresą." + "This homeserver does not support login using email address.": "Šis serveris nepalaiko prisijungimo naudojant el. pašto adresą.", + "Review Sessions": "Peržiūrėti sesijas", + "Setting up keys": "Raktų nustatymas", + "Review where you’re logged in": "Peržiūrėkite kur esate prisijungę", + "Verify all your sessions to ensure your account & messages are safe": "Patvirtinkite visas savo sesijas, kad užtikrintumėte savo paskyros ir žinučių saugumą", + "Review": "Peržiūrėti", + "Message deleted": "Žinutė ištrinta", + "Message deleted by %(name)s": "Žinutė, ištrinta %(name)s", + "Warning: You should only do this on a trusted computer.": "Įspėjimas: Tai atlikite tik saugiame kompiuteryje.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Prieikite prie savo saugių žinučių istorijos ir kryžminio parašo kitų sesijų patvirtinimui tapatybės įvesdami savo atgavimo slaptafrazę.", + "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus.", + "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus", + "Confirm your identity by entering your account password below.": "Patvirtinkite savo tapatybę žemiau įvesdami savo paskyros slaptažodį.", + "Use an email address to recover your account": "Naudokite el. pašto adresą, kad prireikus galėtumėte atgauti paskyrą", + "Passwords don't match": "Slaptažodžiai nesutampa", + "Use lowercase letters, numbers, dashes and underscores only": "Naudokite tik mažąsias raides, brūkšnelius ir pabraukimus", + "Great! This recovery passphrase looks strong enough.": "Puiku! Ši slaptafrazė atrodo pakankamai stipri.", + "That matches!": "Tai sutampa!", + "That doesn't match.": "Tai nesutampa.", + "Confirm your recovery passphrase": "Patvirtinti atgavimo slaptafrazę", + "Your recovery key has been copied to your clipboard, paste it to:": "Jūsų atgavimo raktas buvo nukopijuotas į jūsų iškarpinę, jūs galite:", + "Your recovery key is in your Downloads folder.": "Jūsų atgavimo raktas yra Parsisiuntimų kataloge.", + "Print it and store it somewhere safe": "Atsispausdinti jį ir laikyti saugioje vietoje", + "Save it on a USB key or backup drive": "Išsaugoti jį USB rakte arba atsarginių kopijų diske", + "Copy it to your personal cloud storage": "Nukopijuoti jį į savo asmeninę debesų saugyklą", + "You can now verify your other devices, and other users to keep your chats safe.": "Jūs dabar galite patvirtinti kitus savo įrenginius ir kitus vartotojus, kad jūsų pokalbiai būtų saugūs.", + "Confirm recovery passphrase": "Patvirtinkite atgavimo slaptafrazę", + "You're done!": "Atlikta!" } From 565562ab5c8701c2f63a888de46a58a53c75864a Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Thu, 21 May 2020 11:57:59 +0000 Subject: [PATCH 233/399] Translated using Weblate (Slovak) Currently translated at 67.1% (1558 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index a9111a91a6..6473dad0cf 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -604,7 +604,7 @@ "Stops ignoring a user, showing their messages going forward": "Prestane ignorovať používateľa a začne zobrazovať jeho správy", "Commands": "Príkazy", "Results from DuckDuckGo": "Výsledky z DuckDuckGo", - "Emoji": "Emotikon", + "Emoji": "Emoji", "Notify the whole room": "Oznamovať celú miestnosť", "Room Notification": "Oznámenie miestnosti", "Users": "Používatelia", @@ -1647,8 +1647,22 @@ "Command Autocomplete": "Automatické dopĺňanie príkazov", "Community Autocomplete": "Automatické dopĺňanie skupín", "DuckDuckGo Results": "Výsledky hľadania DuckDuckGo", - "Emoji Autocomplete": "Automatické dopĺňanie emotikonov", + "Emoji Autocomplete": "Automatické dopĺňanie emoji", "Notification Autocomplete": "Automatické dopĺňanie oznámení", "Room Autocomplete": "Automatické dopĺňanie miestností", - "User Autocomplete": "Automatické dopĺňanie používateľov" + "User Autocomplete": "Automatické dopĺňanie používateľov", + "Start": "Začať", + "Verify this session by confirming the following number appears on its screen.": "Overte túto reláciu tým, že zistíte, či sa na jeho obrazovke objaví nasledujúce číslo.", + "Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…": "Čakám na overenie od relácie %(deviceName)s (%(deviceId)s)…", + "Waiting for your other session to verify…": "Čakám na overenie od vašej druhej relácie…", + "Waiting for %(displayName)s to verify…": "Čakám na %(displayName)s, kým nás overí…", + "Cancelling…": "Rušenie…", + "They match": "Zhodujú sa", + "They don't match": "Nezhodujú sa", + "To be secure, do this in person or use a trusted way to communicate.": "Aby ste si boli istý, urobte to osobne alebo použite dôveryhodný spôsob komunikácie.", + "Lock": "Zámok", + "If you can't scan the code above, verify by comparing unique emoji.": "Pokiaľ nemôžete kód vyššie skenovať, overte sa porovnaním jedinečnej kombinácie emoji.", + "Verify by comparing unique emoji.": "Overenie porovnaním jedinečnej kombinácie emoji", + "Verify by emoji": "Overte pomocou emoji", + "Compare emoji": "Porovnajte emoji" } From e3c0b4711686231c4704882dfbfbe72000911c05 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 21 May 2020 11:56:04 -0600 Subject: [PATCH 234/399] Hyphenize algorithm directories --- src/stores/room-list/RoomListStore2.ts | 4 ++-- .../algorithms/{list_ordering => list-ordering}/Algorithm.ts | 0 .../{list_ordering => list-ordering}/ImportanceAlgorithm.ts | 2 +- .../{list_ordering => list-ordering}/NaturalAlgorithm.ts | 2 +- .../algorithms/{list_ordering => list-ordering}/index.ts | 0 .../{tag_sorting => tag-sorting}/AlphabeticAlgorithm.ts | 0 .../algorithms/{tag_sorting => tag-sorting}/IAlgorithm.ts | 0 .../{tag_sorting => tag-sorting}/ManualAlgorithm.ts | 0 .../{tag_sorting => tag-sorting}/RecentAlgorithm.ts | 0 .../algorithms/{tag_sorting => tag-sorting}/index.ts | 0 10 files changed, 4 insertions(+), 4 deletions(-) rename src/stores/room-list/algorithms/{list_ordering => list-ordering}/Algorithm.ts (100%) rename src/stores/room-list/algorithms/{list_ordering => list-ordering}/ImportanceAlgorithm.ts (99%) rename src/stores/room-list/algorithms/{list_ordering => list-ordering}/NaturalAlgorithm.ts (97%) rename src/stores/room-list/algorithms/{list_ordering => list-ordering}/index.ts (100%) rename src/stores/room-list/algorithms/{tag_sorting => tag-sorting}/AlphabeticAlgorithm.ts (100%) rename src/stores/room-list/algorithms/{tag_sorting => tag-sorting}/IAlgorithm.ts (100%) rename src/stores/room-list/algorithms/{tag_sorting => tag-sorting}/ManualAlgorithm.ts (100%) rename src/stores/room-list/algorithms/{tag_sorting => tag-sorting}/RecentAlgorithm.ts (100%) rename src/stores/room-list/algorithms/{tag_sorting => tag-sorting}/index.ts (100%) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 4f38a25d95..881b8fd3cf 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -18,12 +18,12 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/client"; import SettingsStore from "../../settings/SettingsStore"; import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models"; -import { Algorithm } from "./algorithms/list_ordering/Algorithm"; +import { Algorithm } from "./algorithms/list-ordering/Algorithm"; import TagOrderStore from "../TagOrderStore"; import { AsyncStore } from "../AsyncStore"; import { Room } from "matrix-js-sdk/src/models/room"; import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; -import { getListAlgorithmInstance } from "./algorithms/list_ordering"; +import { getListAlgorithmInstance } from "./algorithms/list-ordering"; import { ActionPayload } from "../../dispatcher/payloads"; import defaultDispatcher from "../../dispatcher/dispatcher"; diff --git a/src/stores/room-list/algorithms/list_ordering/Algorithm.ts b/src/stores/room-list/algorithms/list-ordering/Algorithm.ts similarity index 100% rename from src/stores/room-list/algorithms/list_ordering/Algorithm.ts rename to src/stores/room-list/algorithms/list-ordering/Algorithm.ts diff --git a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts similarity index 99% rename from src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts rename to src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index fd5d4c8163..c72cdc2e1c 100644 --- a/src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -19,7 +19,7 @@ import { Algorithm } from "./Algorithm"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomUpdateCause, TagID } from "../../models"; import { ITagMap, SortAlgorithm } from "../models"; -import { sortRoomsWithAlgorithm } from "../tag_sorting"; +import { sortRoomsWithAlgorithm } from "../tag-sorting"; import * as Unread from '../../../../Unread'; /** diff --git a/src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts similarity index 97% rename from src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts rename to src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts index 1265564352..44a501e592 100644 --- a/src/stores/room-list/algorithms/list_ordering/NaturalAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts @@ -16,7 +16,7 @@ limitations under the License. import { Algorithm } from "./Algorithm"; import { ITagMap } from "../models"; -import { sortRoomsWithAlgorithm } from "../tag_sorting"; +import { sortRoomsWithAlgorithm } from "../tag-sorting"; /** * Uses the natural tag sorting algorithm order to determine tag ordering. No diff --git a/src/stores/room-list/algorithms/list_ordering/index.ts b/src/stores/room-list/algorithms/list-ordering/index.ts similarity index 100% rename from src/stores/room-list/algorithms/list_ordering/index.ts rename to src/stores/room-list/algorithms/list-ordering/index.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/AlphabeticAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts similarity index 100% rename from src/stores/room-list/algorithms/tag_sorting/AlphabeticAlgorithm.ts rename to src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/IAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts similarity index 100% rename from src/stores/room-list/algorithms/tag_sorting/IAlgorithm.ts rename to src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/ManualAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts similarity index 100% rename from src/stores/room-list/algorithms/tag_sorting/ManualAlgorithm.ts rename to src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts similarity index 100% rename from src/stores/room-list/algorithms/tag_sorting/RecentAlgorithm.ts rename to src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts diff --git a/src/stores/room-list/algorithms/tag_sorting/index.ts b/src/stores/room-list/algorithms/tag-sorting/index.ts similarity index 100% rename from src/stores/room-list/algorithms/tag_sorting/index.ts rename to src/stores/room-list/algorithms/tag-sorting/index.ts From 41a11bbdb2c0dfe1ac962a78525d8f98a264a9db Mon Sep 17 00:00:00 2001 From: MamasLT Date: Thu, 21 May 2020 18:29:58 +0000 Subject: [PATCH 235/399] Translated using Weblate (Lithuanian) Currently translated at 48.4% (1124 of 2321 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index e96e0e9199..b02cb5c485 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -418,7 +418,7 @@ "A new password must be entered.": "Privalo būti įvestas naujas slaptažodis.", "New passwords must match each other.": "Nauji slaptažodžiai privalo sutapti.", "I have verified my email address": "Aš patvirtinau savo el. pašto adresą", - "Return to login screen": "Grįžti į prisijungimo ekraną", + "Return to login screen": "Grįžti į prisijungimą", "Send Reset Email": "Siųsti atstatymo el. laišką", "Incorrect username and/or password.": "Neteisingas vartotojo vardas ir/arba slaptažodis.", "Please note you are logging into the %(hs)s server, not matrix.org.": "Turėkite omenyje, kad jūs prisijungiate prie %(hs)s serverio, o ne matrix.org.", @@ -1153,5 +1153,10 @@ "Copy it to your personal cloud storage": "Nukopijuoti jį į savo asmeninę debesų saugyklą", "You can now verify your other devices, and other users to keep your chats safe.": "Jūs dabar galite patvirtinti kitus savo įrenginius ir kitus vartotojus, kad jūsų pokalbiai būtų saugūs.", "Confirm recovery passphrase": "Patvirtinkite atgavimo slaptafrazę", - "You're done!": "Atlikta!" + "You're done!": "Atlikta!", + "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Slaptažodžio keitimas šiuo metu anuliuos visapusio šifravimo ranktus visose sesijose, tad šifruotų pokalbių istorija taps neperskaitoma, nebent jūs eksportuosite savo kambarių raktus ir po to importuosite juos atgal. Ateityje ši funkcija bus pataisyta.", + "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Jūsų slaptažodis buvo sėkmingai pakeistas. Jūs nematysite pranešimų kitose sesijose kol iš naujo prie jų neprisijungsite", + "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "El. laiškas buvo išsiųstas į %(emailAddress)s. Kai paspausite jame esančią nuorodą, tada spauskite žemiau.", + "Your password has been reset.": "Jūsų slaptažodis buvo iš naujo nustatytas.", + "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų sesijų ir nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, prisijunkite iš naujo kiekviename įrenginyje." } From a425c5440b16c5d4d002a4e47115d93c15dcdc68 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 21 May 2020 16:34:23 -0600 Subject: [PATCH 236/399] Fix visibility of message timestamps Fixes https://github.com/vector-im/riot-web/issues/13736 This also fixes an unreported but complained about issue regarding the 'always show timestamps' option not working. Looks like this regressed in https://github.com/matrix-org/matrix-react-sdk/pull/4531 when things got shuffled around. --- res/css/views/rooms/_EventTile.scss | 9 ++++++++- res/css/views/rooms/_GroupLayout.scss | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 4297bf809c..20652c6037 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -98,6 +98,7 @@ limitations under the License. .mx_EventTile .mx_MessageTimestamp { display: block; + visibility: hidden; white-space: nowrap; left: 0px; text-align: center; @@ -158,10 +159,16 @@ limitations under the License. } // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) +// The first set is to handle the 'group layout' (default) and the second for the IRC layout .mx_EventTile_last > div > a > .mx_MessageTimestamp, .mx_EventTile:hover > div > a > .mx_MessageTimestamp, .mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp, -.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp { +.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp, + +.mx_IRCLayout .mx_EventTile_last > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile:hover > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile.mx_EventTile_actionBarFocused > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile.focus-visible:focus-within > a > .mx_MessageTimestamp { visibility: visible; } diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 40440f7d49..928ea75a79 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -34,7 +34,6 @@ $left-gutter: 65px; } .mx_MessageTimestamp { - visibility: hidden; position: absolute; width: 46px; /* 8 + 30 (avatar) + 8 */ } From ef4d0a805194e627606d3306f3b64fb22e1d87d8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 May 2020 10:33:19 +0100 Subject: [PATCH 237/399] Make linter happy --- res/css/views/rooms/_EventTile.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 20652c6037..40a80f17bb 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -164,7 +164,6 @@ limitations under the License. .mx_EventTile:hover > div > a > .mx_MessageTimestamp, .mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp, .mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp, - .mx_IRCLayout .mx_EventTile_last > a > .mx_MessageTimestamp, .mx_IRCLayout .mx_EventTile:hover > a > .mx_MessageTimestamp, .mx_IRCLayout .mx_EventTile.mx_EventTile_actionBarFocused > a > .mx_MessageTimestamp, From 6bfa31cd47d626fe10c9148f16557c6a2a86bedd Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 May 2020 10:34:00 +0100 Subject: [PATCH 238/399] run i18n which is unhappy about 'create room' for some reason --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f79d93b98f..96ccf1589d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1127,7 +1127,6 @@ "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", - "Create room": "Create room", "This room": "This room", "Joining room …": "Joining room …", "Loading …": "Loading …", From cb270ba0d5a0c6478a38e0dd92577393e20e9082 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Thu, 21 May 2020 21:59:22 +0000 Subject: [PATCH 239/399] Translated using Weblate (Esperanto) Currently translated at 100.0% (2322 of 2322 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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 89c75532df..f201d5a117 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2419,5 +2419,6 @@ "Size must be a number": "Grando devas esti nombro", "Custom font size can only be between %(min)s pt and %(max)s pt": "Propra grando de tiparo povas interi nur %(min)s punktojn kaj %(max)s punktojn", "Use between %(min)s pt and %(max)s pt": "Uzi inter %(min)s punktoj kaj %(max)s punktoj", - "Appearance": "Aspekto" + "Appearance": "Aspekto", + "Use the improved room list (in development - refresh to apply changes)": "Uzi la plibonigitan liston de ĉambroj (ankoraŭ evoluigate – aktualigu la paĝon por efektivigi ŝanĝojn)" } From f0c8f20cf6a4ea06e421adc50152337f9453d8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 21 May 2020 20:03:09 +0000 Subject: [PATCH 240/399] Translated using Weblate (Estonian) Currently translated at 54.8% (1273 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 3d4b7ec888..d00525b420 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1242,5 +1242,39 @@ "Confirm account deactivation": "Kinnita konto sulgemine", "There was a problem communicating with the server. Please try again.": "Serveriühenduses tekkis viga. Palun proovi uuesti.", "Server did not return valid authentication information.": "Serveri saadetud vastuses ei olnud kehtivat autentimisteavet.", - "Please fill why you're reporting.": "Palun kirjelda veateate põhjust." + "Please fill why you're reporting.": "Palun kirjelda veateate põhjust.", + "Something went wrong trying to invite the users.": "Kasutajatele kutse saatmisel läks midagi viltu.", + "We couldn't invite those users. Please check the users you want to invite and try again.": "Meil ei õnnestunud neile kasutajatele kutset saata. Palun kontrolli, keda soovid kutsuda ning proovi uuesti.", + "Failed to find the following users": "Järgnevaid kasutajaid ei õnnestunud leida", + "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Järgmisi kasutajanimesid pole olemas või on vigaselt kirjas ning seega ei saa neile kutset saata: %(csvNames)s", + "Recently Direct Messaged": "Viimased otsesõnumite saajad", + "Invite someone using their name, username (like ), email address or share this room.": "Kutsu kedagi tema nime, kasutajanime (nagu ), e-posti aadressi alusel või jaga seda jututuba.", + "You added a new session '%(displayName)s', which is requesting encryption keys.": "Sa oled lisanud uue sessiooni '%(displayName)s', mis küsib krüptimisvõtmeid.", + "Start verification": "Alusta verifitseerimist", + "Share without verifying": "Jaga ilma verifitseerimata", + "Loading session info...": "Laen sessiooniteavet…", + "Encryption key request": "Krüptimisvõtmete päring", + "Upload completed": "Üleslaadimine valmis", + "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 kasutab varasemaga võrreldes 3-5 korda vähem mälu, sest laeb teavet kasutajate kohta vaid siis, kui vaja. Palun oota hetke, kuni sünkroniseerime andmeid serveriga!", + "Updating Riot": "Uuenda Riot'it", + "I don't want my encrypted messages": "Ma ei soovi oma krüptitud sõnumeid", + "Manually export keys": "Ekspordi võtmed käsitsi", + "You'll lose access to your encrypted messages": "Sa kaotad ligipääsu oma krüptitud sõnumitele", + "Are you sure you want to sign out?": "Kas sa oled kindel, et soovid välja logida?", + "Upload %(count)s other files|one": "Lae üles %(count)s muu fail", + "Cancel All": "Tühista kõik", + "Upload Error": "Üleslaadimise viga", + "Verify other session": "Verifitseeri teine sessioon", + "Verification Request": "Verifitseerimispäring", + "A widget would like to verify your identity": "Vidin soovib verifitseerida sinu isikut", + "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.": "Vidin %(widgetUrl)s saidist soovib verifitseerida sinu isikut. Kui sa seda lubad, siis vidin verifitseerib vaid sinu kasutajatunnuse, kuid ei saa teha toiminguid sinuna.", + "Remember my selection for this widget": "Jäta meelde minu valik selle vidina kohta", + "Allow": "Luba", + "Deny": "Keela", + "Unable to restore backup": "Varukoopiast taastamine ei õnenstu", + "No backup found!": "Varukoopiat ei leidunud!", + "Keys restored": "Krüptimise võtmed on taastatud", + "Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s sessiooni dekrüptimine ei õnnestunud!", + "Successfully restored %(sessionCount)s keys": "%(sessionCount)s sessiooni võtme taastamine õnnestus", + "Warning: you should only set up key backup from a trusted computer.": "Hoiatus: sa peaksid võtmete varunduse seadistama vaid usaldusväärsest arvutist." } From 19667964a7e1a826e086722ded126bf9d476e665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 22 May 2020 06:36:51 +0000 Subject: [PATCH 241/399] Translated using Weblate (French) Currently translated at 100.0% (2322 of 2322 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 ee3c398e86..66bb97118f 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2445,5 +2445,6 @@ "Size must be a number": "La taille doit être un nombre", "Custom font size can only be between %(min)s pt and %(max)s pt": "La taille de police personnalisée doit être comprise entre %(min)s pt et %(max)s pt", "Use between %(min)s pt and %(max)s pt": "Utiliser entre %(min)s pt et %(max)s pt", - "Appearance": "Apparence" + "Appearance": "Apparence", + "Use the improved room list (in development - refresh to apply changes)": "Utiliser la liste de salons améliorée (en développement − actualisez pour appliquer les changements)" } From f00ce9aace4a780e6a393eaef7128c932f9377e4 Mon Sep 17 00:00:00 2001 From: XoseM Date: Fri, 22 May 2020 09:18:06 +0000 Subject: [PATCH 242/399] Translated using Weblate (Galician) Currently translated at 43.7% (1014 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index d433d58146..48eb1338b4 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -986,5 +986,45 @@ "Review where you’re logged in": "Revisar onde estás conectada", "New login. Was this you?": "Nova conexión. Foches ti?", "Name or Matrix ID": "Nome ou ID Matrix", - "Identity server has no terms of service": "O servidor de identidade non ten termos dos servizo" + "Identity server has no terms of service": "O servidor de identidade non ten termos dos servizo", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Esta acción precisa acceder ao servidor de indentidade para validar o enderezo de email ou o número de teléfono, pero o servidor non publica os seus termos do servizo.", + "Only continue if you trust the owner of the server.": "Continúa se realmente confías no dono do servidor.", + "Trust": "Confiar", + "%(name)s is requesting verification": "%(name)s está pedindo a verificación", + "Use your account or create a new one to continue.": "Usa a túa conta ou crea unha nova para continuar.", + "Create Account": "Crear conta", + "Custom (%(level)s)": "Personalizado (%(level)s)", + "Failed to invite users to the room:": "Fallo a convidar a persoas a sala:", + "Messages": "Mensaxes", + "Actions": "Accións", + "Other": "Outro", + "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Anteponse ¯\\_(ツ)_/¯ a mensaxe en texto plano", + "Sends a message as plain text, without interpreting it as markdown": "Envía unha mensaxe como texto plano, sen interpretalo como markdown", + "Sends a message as html, without interpreting it as markdown": "Envía unha mensaxe como html, sen interpretalo como markdown", + "Upgrades a room to a new version": "Subir a sala de versión", + "You do not have the required permissions to use this command.": "Non tes os permisos suficientes para usar este comando.", + "Error upgrading room": "Fallo ao actualizar a sala", + "Double check that your server supports the room version chosen and try again.": "Comproba ben que o servidor soporta a versión da sala escollida e inténtao outra vez.", + "Changes your display nickname in the current room only": "Cambia o teu nome mostrado só para esta esta sala", + "Changes the avatar of the current room": "Cambia o avatar da sala actual", + "Changes your avatar in this current room only": "Cambia o teu avatar só nesta sala", + "Changes your avatar in all rooms": "Cambia o teu avatar en todas as salas", + "Gets or sets the room topic": "Obtén ou establece o asunto da sala", + "Failed to set topic": "Fallo ao establecer asunto", + "This room has no topic.": "Esta sala non ten asunto.", + "Sets the room name": "Establecer nome da sala", + "Use an identity server": "Usar un servidor de identidade", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Usar un servidor de identidade para convidar por email. Preme continuar para usar o servidor de identidade por omisión (%(defaultIdentityServerName)s) ou cambiao en Axustes.", + "Use an identity server to invite by email. Manage in Settings.": "Usar un servidor de indentidade para convidar por email. Xestionao en Axustes.", + "Command failed": "O comando fallou", + "Could not find user in room": "Non se atopa a usuaria na sala", + "Adds a custom widget by URL to the room": "Engade un widget por URL personalizado a sala", + "Please supply a widget URL or embed code": "Proporciona o URL do widget ou incrusta o código", + "Please supply a https:// or http:// widget URL": "Escribe un https:// ou http:// como URL do widget", + "You cannot modify widgets in this room.": "Non podes modificar os widgets desta sala.", + "Unknown (user, session) pair:": "Par descoñecido (usuaria, sesión):", + "Session already verified!": "A sesión xa está verificada!", + "WARNING: Session already verified, but keys do NOT MATCH!": "AVISO: xa está verificada a sesión, pero as chaves NON CONCORDAN!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "AVISO: FALLOU A VERIFICACIÓN DAS CHAVES! A chave de firma para %(userId)s na sesión %(deviceId)s é \"%(fprint)s\" que non concordan coa chave proporcionada \"%(fingerprint)s\". Esto podería significar que as túas comunicacións foron interceptadas!", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "A chave de firma proporcionada concorda coa chave de firma recibida desde a sesión %(deviceId)s de %(userId)s. Sesión marcada como verificada." } From ead1fc59fabb1a86a7e1763661ef48cb53493da2 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 22 May 2020 08:08:45 +0000 Subject: [PATCH 243/399] Translated using Weblate (Italian) Currently translated at 100.0% (2322 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 1fd6d7dc0c..7f6d3bf1e2 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2440,5 +2440,6 @@ "Size must be a number": "La dimensione deve essere un numero", "Custom font size can only be between %(min)s pt and %(max)s pt": "La dimensione del carattere personalizzata può solo essere tra %(min)s pt e %(max)s pt", "Use between %(min)s pt and %(max)s pt": "Usa tra %(min)s pt e %(max)s pt", - "Appearance": "Aspetto" + "Appearance": "Aspetto", + "Use the improved room list (in development - refresh to apply changes)": "Usa l'elenco stanze migliorato (in sviluppo - ricarica per applicare le modifiche)" } From fe357e530dc482b5338a59a9e74a36727797252c Mon Sep 17 00:00:00 2001 From: MamasLT Date: Thu, 21 May 2020 21:54:40 +0000 Subject: [PATCH 244/399] Translated using Weblate (Lithuanian) Currently translated at 48.5% (1127 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index b02cb5c485..c6f3abef0f 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1158,5 +1158,8 @@ "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Jūsų slaptažodis buvo sėkmingai pakeistas. Jūs nematysite pranešimų kitose sesijose kol iš naujo prie jų neprisijungsite", "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "El. laiškas buvo išsiųstas į %(emailAddress)s. Kai paspausite jame esančią nuorodą, tada spauskite žemiau.", "Your password has been reset.": "Jūsų slaptažodis buvo iš naujo nustatytas.", - "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų sesijų ir nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, prisijunkite iš naujo kiekviename įrenginyje." + "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų sesijų ir nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, prisijunkite iš naujo kiekviename įrenginyje.", + "Show more": "Rodyti daugiau", + "Log in to your new account.": "Prisijunkite į savo naują paskyrą.", + "Registration Successful": "Registracija sėkminga" } From 67ffe94df4c0774a97748e886b9c88b696cf843a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 11:18:14 +0100 Subject: [PATCH 245/399] Fix typo and improve error context Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 2 +- src/stores/RoomViewStore.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 8e343fe08f..39cd497098 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1676,7 +1676,7 @@ export default createReactClass({ const ErrorBoundary = sdk.getComponent("elements.ErrorBoundary"); if (!this.state.room) { - const loading = this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading; + const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading; if (loading) { // Assume preview loading if we don't have a ready client or a room ID (still resolving the alias) const previewLoading = !this.state.matrixClientIsReady || !this.state.roomId || this.state.peekLoading; diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 1958b9539f..a38445c89b 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -215,7 +215,7 @@ class RoomViewStore extends Store { storeRoomAliasInCache(payload.room_alias, result.room_id); roomId = result.room_id; } catch (err) { - console.error(err); + console.error("RVS failed to get room id for alias: ", err); dis.dispatch({ action: 'view_room_error', room_id: null, From badda5ae815d77165a90c6f5804192c8e1321a3d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 19 May 2020 15:40:26 +0100 Subject: [PATCH 246/399] Remove SSSS key upgrade check from rageshake This code doesn't exist anymore as the SSSS symmetric upgrade stuff has been removed. Fixes https://github.com/vector-im/riot-web/issues/13715 --- src/rageshake/submit-rageshake.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index e5027e0d37..9f9d7898cb 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -133,7 +133,6 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp body.append("cross_signing_supported_by_hs", String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"))); body.append("cross_signing_ready", String(await client.isCrossSigningReady())); - body.append("ssss_key_needs_upgrade", String(await client.secretStorageKeyNeedsUpgrade())); } } From 0201655538d44c05e0135e6db92caf060ee166e7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 21 May 2020 12:26:27 +0100 Subject: [PATCH 247/399] Fix key backup restore with SSSS The room / session ID params come after the backupInfo for restoring from SSSS so the options object was being passed into the wrong param. Roll on TypeScript. This meant restoring backups worked fine when the key was cached but failed when it wasn't. Regressed in https://github.com/matrix-org/matrix-react-sdk/pull/4507 --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 7e51e76f6c..a16202ed93 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -201,7 +201,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { // `accessSecretStorage` may prompt for storage access as needed. const recoverInfo = await accessSecretStorage(async () => { return MatrixClientPeg.get().restoreKeyBackupWithSecretStorage( - this.state.backupInfo, + this.state.backupInfo, undefined, undefined, { progressCallback: this._progressCallback }, ); }); From dc37469808d23000f3a7c9b6f184b07a31677379 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 12:47:40 +0100 Subject: [PATCH 248/399] Convert ToastContainer and ToastStore to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 1 + src/@types/global.d.ts | 3 + src/components/structures/MatrixChat.tsx | 2 +- .../{ToastContainer.js => ToastContainer.tsx} | 16 ++-- src/stores/ToastStore.js | 73 ------------------ src/stores/ToastStore.ts | 77 +++++++++++++++++++ yarn.lock | 5 ++ 7 files changed, 98 insertions(+), 79 deletions(-) rename src/components/structures/{ToastContainer.js => ToastContainer.tsx} (87%) delete mode 100644 src/stores/ToastStore.js create mode 100644 src/stores/ToastStore.ts diff --git a/package.json b/package.json index 193fb86218..440fe3beac 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@types/classnames": "^2.2.10", "@types/flux": "^3.1.9", "@types/modernizr": "^3.5.3", + "@types/node": "^12.12.41", "@types/qrcode": "^1.3.4", "@types/react": "16.9", "@types/zxcvbn": "^4.4.0", diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index e6e339d067..6c62000143 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -15,6 +15,7 @@ limitations under the License. */ import * as ModernizrStatic from "modernizr"; +import ToastStore from "../stores/ToastStore"; declare global { interface Window { @@ -22,6 +23,8 @@ declare global { Olm: { init: () => Promise; }; + + mx_ToastStore: ToastStore; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 48dc72f4fa..89db30c7b4 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1559,7 +1559,7 @@ export default class MatrixChat extends React.PureComponent { icon: "verification", props: {request}, component: sdk.getComponent("toasts.VerificationRequestToast"), - priority: ToastStore.PRIORITY_REALTIME, + priority: 95, }); } }); diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.tsx similarity index 87% rename from src/components/structures/ToastContainer.js rename to src/components/structures/ToastContainer.tsx index 283fbdd96a..9440aa3463 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.tsx @@ -16,13 +16,19 @@ limitations under the License. import * as React from "react"; import { _t } from '../../languageHandler'; -import ToastStore from "../../stores/ToastStore"; +import ToastStore, {IToast} from "../../stores/ToastStore"; import classNames from "classnames"; -export default class ToastContainer extends React.Component { - constructor() { - super(); - this.state = {toasts: ToastStore.sharedInstance().getToasts()}; +interface IState { + toasts: IToast[]; +} + +export default class ToastContainer extends React.Component<{}, IState> { + constructor(props, context) { + super(props, context); + this.state = { + toasts: ToastStore.sharedInstance().getToasts(), + }; // Start listening here rather than in componentDidMount because // toasts may dismiss themselves in their didMount if they find diff --git a/src/stores/ToastStore.js b/src/stores/ToastStore.js deleted file mode 100644 index 8901736739..0000000000 --- a/src/stores/ToastStore.js +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2020 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 EventEmitter from 'events'; - -/** - * Holds the active toasts - */ -export default class ToastStore extends EventEmitter { - static PRIORITY_REALTIME = 0; - static PRIORITY_DEFAULT = 1; - static PRIORITY_LOW = 2; - - static sharedInstance() { - if (!global.mx_ToastStore) global.mx_ToastStore = new ToastStore(); - return global.mx_ToastStore; - } - - constructor() { - super(); - this._dispatcherRef = null; - this._toasts = []; - } - - reset() { - this._toasts = []; - } - - /** - * Add or replace a toast - * If a toast with the same toastKey already exists, the given toast will replace it - * Toasts are always added underneath any toasts of the same priority, so existing - * toasts stay at the top unless a higher priority one arrives (better to not change the - * toast unless necessary). - * - * @param {boject} newToast The new toast - */ - addOrReplaceToast(newToast) { - if (newToast.priority === undefined) newToast.priority = ToastStore.PRIORITY_DEFAULT; - - const oldIndex = this._toasts.findIndex(t => t.key === newToast.key); - if (oldIndex === -1) { - let newIndex = this._toasts.length; - while (newIndex > 0 && this._toasts[newIndex - 1].priority > newToast.priority) --newIndex; - this._toasts.splice(newIndex, 0, newToast); - } else { - this._toasts[oldIndex] = newToast; - } - this.emit('update'); - } - - dismissToast(key) { - this._toasts = this._toasts.filter(t => t.key !== key); - this.emit('update'); - } - - getToasts() { - return this._toasts; - } -} diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts new file mode 100644 index 0000000000..4f6d2963c5 --- /dev/null +++ b/src/stores/ToastStore.ts @@ -0,0 +1,77 @@ +/* +Copyright 2020 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 EventEmitter from "events"; +import React, {JSXElementConstructor} from "react"; + +export interface IToast> { + key: string; + // higher priority number will be shown on top of lower priority + priority: number; + title: string; + icon?: string; + component: C; + props?: React.ComponentProps; +} + +/** + * Holds the active toasts + */ +export default class ToastStore extends EventEmitter { + private toasts: IToast[] = []; + + static sharedInstance() { + if (!window.mx_ToastStore) window.mx_ToastStore = new ToastStore(); + return window.mx_ToastStore; + } + + reset() { + this.toasts = []; + } + + /** + * Add or replace a toast + * If a toast with the same toastKey already exists, the given toast will replace it + * Toasts are always added underneath any toasts of the same priority, so existing + * toasts stay at the top unless a higher priority one arrives (better to not change the + * toast unless necessary). + * + * @param {object} newToast The new toast + */ + addOrReplaceToast>(newToast: IToast) { + const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); + if (oldIndex === -1) { + let newIndex = this.toasts.length; + while (newIndex > 0 && this.toasts[newIndex - 1].priority > newToast.priority) --newIndex; + this.toasts.splice(newIndex, 0, newToast); + } else { + this.toasts[oldIndex] = newToast; + } + this.emit('update'); + } + + dismissToast(key) { + const length = this.toasts.length; + this.toasts = this.toasts.filter(t => t.key !== key); + if (length !== this.toasts.length) { + this.emit('update'); + } + } + + getToasts() { + return this.toasts; + } +} diff --git a/yarn.lock b/yarn.lock index 6cdc771c5b..340d0b4454 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1280,6 +1280,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ== +"@types/node@^12.12.41": + version "12.12.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.42.tgz#d0d1149336bd07540dd1ea576692829d575dec34" + integrity sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" From 3bf5e003a154ff54704f5873ff81d51688d60052 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 12:54:03 +0100 Subject: [PATCH 249/399] Convert DeviceListener to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 2 + src/{DeviceListener.js => DeviceListener.ts} | 91 +++++++++----------- 2 files changed, 45 insertions(+), 48 deletions(-) rename src/{DeviceListener.js => DeviceListener.ts} (81%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 6c62000143..bcf08b26bb 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -16,6 +16,7 @@ limitations under the License. import * as ModernizrStatic from "modernizr"; import ToastStore from "../stores/ToastStore"; +import DeviceListener from "../DeviceListener"; declare global { interface Window { @@ -25,6 +26,7 @@ declare global { }; mx_ToastStore: ToastStore; + mx_DeviceListener: DeviceListener; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/DeviceListener.js b/src/DeviceListener.ts similarity index 81% rename from src/DeviceListener.js rename to src/DeviceListener.ts index 27caba971e..ce1f781dad 100644 --- a/src/DeviceListener.js +++ b/src/DeviceListener.ts @@ -29,28 +29,23 @@ function toastKey(deviceId) { } export default class DeviceListener { + // device IDs for which the user has dismissed the verify toast ('Later') + private dismissed = new Set(); + // has the user dismissed any of the various nag toasts to setup encryption on this device? + private dismissedThisDeviceToast = false; + // cache of the key backup info + private keyBackupInfo: object = null; + private keyBackupFetchedAt: number = null; + // We keep a list of our own device IDs so we can batch ones that were already + // there the last time the app launched into a single toast, but display new + // ones in their own toasts. + private ourDeviceIdsAtStart: Set = null; + // The set of device IDs we're currently displaying toasts for + private displayingToastsForDeviceIds = new Set(); + static sharedInstance() { - if (!global.mx_DeviceListener) global.mx_DeviceListener = new DeviceListener(); - return global.mx_DeviceListener; - } - - constructor() { - // device IDs for which the user has dismissed the verify toast ('Later') - this._dismissed = new Set(); - // has the user dismissed any of the various nag toasts to setup encryption on this device? - this._dismissedThisDeviceToast = false; - - // cache of the key backup info - this._keyBackupInfo = null; - this._keyBackupFetchedAt = null; - - // We keep a list of our own device IDs so we can batch ones that were already - // there the last time the app launched into a single toast, but display new - // ones in their own toasts. - this._ourDeviceIdsAtStart = null; - - // The set of device IDs we're currently displaying toasts for - this._displayingToastsForDeviceIds = new Set(); + if (!window.mx_DeviceListener) window.mx_DeviceListener = new DeviceListener(); + return window.mx_DeviceListener; } start() { @@ -74,12 +69,12 @@ export default class DeviceListener { MatrixClientPeg.get().removeListener('accountData', this._onAccountData); MatrixClientPeg.get().removeListener('sync', this._onSync); } - this._dismissed.clear(); - this._dismissedThisDeviceToast = false; - this._keyBackupInfo = null; - this._keyBackupFetchedAt = null; - this._ourDeviceIdsAtStart = null; - this._displayingToastsForDeviceIds = new Set(); + this.dismissed.clear(); + this.dismissedThisDeviceToast = false; + this.keyBackupInfo = null; + this.keyBackupFetchedAt = null; + this.ourDeviceIdsAtStart = null; + this.displayingToastsForDeviceIds = new Set(); } /** @@ -87,29 +82,29 @@ export default class DeviceListener { * * @param {String[]} deviceIds List of device IDs to dismiss notifications for */ - async dismissUnverifiedSessions(deviceIds) { + async dismissUnverifiedSessions(deviceIds: string[]) { for (const d of deviceIds) { - this._dismissed.add(d); + this.dismissed.add(d); } this._recheck(); } dismissEncryptionSetup() { - this._dismissedThisDeviceToast = true; + this.dismissedThisDeviceToast = true; this._recheck(); } _ensureDeviceIdsAtStartPopulated() { - if (this._ourDeviceIdsAtStart === null) { + if (this.ourDeviceIdsAtStart === null) { const cli = MatrixClientPeg.get(); - this._ourDeviceIdsAtStart = new Set( + this.ourDeviceIdsAtStart = new Set( cli.getStoredDevicesForUser(cli.getUserId()).map(d => d.deviceId), ); } } - _onWillUpdateDevices = async (users, initialFetch) => { + _onWillUpdateDevices = async (users: string[], initialFetch?: boolean) => { // If we didn't know about *any* devices before (ie. it's fresh login), // then they are all pre-existing devices, so ignore this and set the // devicesAtStart list to the devices that we see after the fetch. @@ -122,17 +117,17 @@ export default class DeviceListener { // before we download any new ones. } - _onDevicesUpdated = (users) => { + _onDevicesUpdated = (users: string[]) => { if (!users.includes(MatrixClientPeg.get().getUserId())) return; this._recheck(); } - _onDeviceVerificationChanged = (userId) => { + _onDeviceVerificationChanged = (userId: string) => { if (userId !== MatrixClientPeg.get().getUserId()) return; this._recheck(); } - _onUserTrustStatusChanged = (userId, trustLevel) => { + _onUserTrustStatusChanged = (userId: string) => { if (userId !== MatrixClientPeg.get().getUserId()) return; this._recheck(); } @@ -163,11 +158,11 @@ export default class DeviceListener { // & cache the result async _getKeyBackupInfo() { const now = (new Date()).getTime(); - if (!this._keyBackupInfo || this._keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) { - this._keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); - this._keyBackupFetchedAt = now; + if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) { + this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); + this.keyBackupFetchedAt = now; } - return this._keyBackupInfo; + return this.keyBackupInfo; } async _recheck() { @@ -186,7 +181,7 @@ export default class DeviceListener { const crossSigningReady = await cli.isCrossSigningReady(); - if (this._dismissedThisDeviceToast) { + if (this.dismissedThisDeviceToast) { ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); } else { if (!crossSigningReady) { @@ -239,20 +234,20 @@ export default class DeviceListener { // (technically could just be a boolean: we don't actually // need to remember the device IDs, but for the sake of // symmetry...). - const oldUnverifiedDeviceIds = new Set(); + const oldUnverifiedDeviceIds = new Set(); // Unverified devices that have appeared since then - const newUnverifiedDeviceIds = new Set(); + const newUnverifiedDeviceIds = new Set(); // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts if (crossSigningReady) { const devices = cli.getStoredDevicesForUser(cli.getUserId()); for (const device of devices) { - if (device.deviceId == cli.deviceId) continue; + if (device.deviceId === cli.deviceId) continue; const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId); - if (!deviceTrust.isCrossSigningVerified() && !this._dismissed.has(device.deviceId)) { - if (this._ourDeviceIdsAtStart.has(device.deviceId)) { + if (!deviceTrust.isCrossSigningVerified() && !this.dismissed.has(device.deviceId)) { + if (this.ourDeviceIdsAtStart.has(device.deviceId)) { oldUnverifiedDeviceIds.add(device.deviceId); } else { newUnverifiedDeviceIds.add(device.deviceId); @@ -289,12 +284,12 @@ export default class DeviceListener { } // ...and hide any we don't need any more - for (const deviceId of this._displayingToastsForDeviceIds) { + for (const deviceId of this.displayingToastsForDeviceIds) { if (!newUnverifiedDeviceIds.has(deviceId)) { ToastStore.sharedInstance().dismissToast(toastKey(deviceId)); } } - this._displayingToastsForDeviceIds = newUnverifiedDeviceIds; + this.displayingToastsForDeviceIds = newUnverifiedDeviceIds; } } From b21e5ba10b313ebc492b2241d808f917814a5706 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 12:57:48 +0100 Subject: [PATCH 250/399] Set new granular priorities Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/DeviceListener.ts | 6 +++++- src/components/structures/MatrixChat.tsx | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index ce1f781dad..1ecaca9b40 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -197,6 +197,7 @@ export default class DeviceListener { icon: "verification_warning", props: {kind: 'verify_this_session'}, component: sdk.getComponent("toasts.SetupEncryptionToast"), + priority: 95, }); } else { const backupInfo = await this._getKeyBackupInfo(); @@ -208,6 +209,7 @@ export default class DeviceListener { icon: "verification_warning", props: {kind: 'upgrade_encryption'}, component: sdk.getComponent("toasts.SetupEncryptionToast"), + priority: 40, }); } else { // No cross-signing or key backup on account (set up encryption) @@ -217,6 +219,7 @@ export default class DeviceListener { icon: "verification_warning", props: {kind: 'set_up_encryption'}, component: sdk.getComponent("toasts.SetupEncryptionToast"), + priority: 40, }); } } @@ -262,11 +265,11 @@ export default class DeviceListener { key: OTHER_DEVICES_TOAST_KEY, title: _t("Review where you’re logged in"), icon: "verification_warning", - priority: ToastStore.PRIORITY_LOW, props: { deviceIds: oldUnverifiedDeviceIds, }, component: sdk.getComponent("toasts.BulkUnverifiedSessionsToast"), + priority: 50, }); } else { ToastStore.sharedInstance().dismissToast(OTHER_DEVICES_TOAST_KEY); @@ -280,6 +283,7 @@ export default class DeviceListener { icon: "verification_warning", props: { deviceId }, component: sdk.getComponent("toasts.UnverifiedSessionToast"), + priority: 80, }); } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 89db30c7b4..e6a56c683f 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1559,7 +1559,7 @@ export default class MatrixChat extends React.PureComponent { icon: "verification", props: {request}, component: sdk.getComponent("toasts.VerificationRequestToast"), - priority: 95, + priority: 90, }); } }); From 14cee413603ce4fc36bdf1ac2f2b0551fe6b4516 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 13:29:53 +0100 Subject: [PATCH 251/399] Convert things to Typescript and re-use a generic component Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/DeviceListener.ts | 111 ++++++------------ .../toasts/BulkUnverifiedSessionsToast.js | 56 --------- src/components/views/toasts/GenericToast.tsx | 42 +++++++ .../views/toasts/SetupEncryptionToast.js | 88 -------------- .../views/toasts/UnverifiedSessionToast.js | 66 ----------- ...tToast.js => VerificationRequestToast.tsx} | 48 +++++--- src/toasts/BulkUnverifiedSessionsToast.ts | 58 +++++++++ src/toasts/SetupEncryptionToast.ts | 106 +++++++++++++++++ src/toasts/UnverifiedSessionToast.ts | 70 +++++++++++ 9 files changed, 342 insertions(+), 303 deletions(-) delete mode 100644 src/components/views/toasts/BulkUnverifiedSessionsToast.js create mode 100644 src/components/views/toasts/GenericToast.tsx delete mode 100644 src/components/views/toasts/SetupEncryptionToast.js delete mode 100644 src/components/views/toasts/UnverifiedSessionToast.js rename src/components/views/toasts/{VerificationRequestToast.js => VerificationRequestToast.tsx} (86%) create mode 100644 src/toasts/BulkUnverifiedSessionsToast.ts create mode 100644 src/toasts/SetupEncryptionToast.ts create mode 100644 src/toasts/UnverifiedSessionToast.ts diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 1ecaca9b40..ca51b5ac1c 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -14,19 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixClientPeg } from './MatrixClientPeg'; +import {MatrixClientPeg} from './MatrixClientPeg'; import SettingsStore from './settings/SettingsStore'; -import * as sdk from './index'; -import { _t } from './languageHandler'; -import ToastStore from './stores/ToastStore'; +import { + hideToast as hideBulkUnverifiedSessionsToast, + showToast as showBulkUnverifiedSessionsToast +} from "./toasts/BulkUnverifiedSessionsToast"; +import { + hideToast as hideSetupEncryptionToast, + Kind as SetupKind, + Kind, + showToast as showSetupEncryptionToast +} from "./toasts/SetupEncryptionToast"; +import { + hideToast as hideUnverifiedSessionsToast, + showToast as showUnverifiedSessionsToast +} from "./toasts/UnverifiedSessionToast"; const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000; -const THIS_DEVICE_TOAST_KEY = 'setupencryption'; -const OTHER_DEVICES_TOAST_KEY = 'reviewsessions'; - -function toastKey(deviceId) { - return "unverified_session_" + deviceId; -} export default class DeviceListener { // device IDs for which the user has dismissed the verify toast ('Later') @@ -82,7 +87,7 @@ export default class DeviceListener { * * @param {String[]} deviceIds List of device IDs to dismiss notifications for */ - async dismissUnverifiedSessions(deviceIds: string[]) { + async dismissUnverifiedSessions(deviceIds: Iterable) { for (const d of deviceIds) { this.dismissed.add(d); } @@ -181,51 +186,25 @@ export default class DeviceListener { const crossSigningReady = await cli.isCrossSigningReady(); - if (this.dismissedThisDeviceToast) { - ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); + if (this.dismissedThisDeviceToast || crossSigningReady) { + hideSetupEncryptionToast(); } else { - if (!crossSigningReady) { - // make sure our keys are finished downlaoding - await cli.downloadKeys([cli.getUserId()]); - // cross signing isn't enabled - nag to enable it - // There are 3 different toasts for: - if (cli.getStoredCrossSigningForUser(cli.getUserId())) { - // Cross-signing on account but this device doesn't trust the master key (verify this session) - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Verify this session"), - icon: "verification_warning", - props: {kind: 'verify_this_session'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - priority: 95, - }); - } else { - const backupInfo = await this._getKeyBackupInfo(); - if (backupInfo) { - // No cross-signing on account but key backup available (upgrade encryption) - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Encryption upgrade available"), - icon: "verification_warning", - props: {kind: 'upgrade_encryption'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - priority: 40, - }); - } else { - // No cross-signing or key backup on account (set up encryption) - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Set up encryption"), - icon: "verification_warning", - props: {kind: 'set_up_encryption'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - priority: 40, - }); - } - } + // make sure our keys are finished downloading + await cli.downloadKeys([cli.getUserId()]); + // cross signing isn't enabled - nag to enable it + // There are 3 different toasts for: + if (cli.getStoredCrossSigningForUser(cli.getUserId())) { + // Cross-signing on account but this device doesn't trust the master key (verify this session) + showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION); } else { - // cross-signing is ready, and we don't need to upgrade encryption - ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); + const backupInfo = await this._getKeyBackupInfo(); + if (backupInfo) { + // No cross-signing on account but key backup available (upgrade encryption) + showSetupEncryptionToast(Kind.UPGRADE_ENCRYPTION); + } else { + // No cross-signing or key backup on account (set up encryption) + showSetupEncryptionToast(Kind.SET_UP_ENCRYPTION); + } } } @@ -261,36 +240,20 @@ export default class DeviceListener { // Display or hide the batch toast for old unverified sessions if (oldUnverifiedDeviceIds.size > 0) { - ToastStore.sharedInstance().addOrReplaceToast({ - key: OTHER_DEVICES_TOAST_KEY, - title: _t("Review where you’re logged in"), - icon: "verification_warning", - props: { - deviceIds: oldUnverifiedDeviceIds, - }, - component: sdk.getComponent("toasts.BulkUnverifiedSessionsToast"), - priority: 50, - }); + showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds); } else { - ToastStore.sharedInstance().dismissToast(OTHER_DEVICES_TOAST_KEY); + hideBulkUnverifiedSessionsToast(); } // Show toasts for new unverified devices if they aren't already there for (const deviceId of newUnverifiedDeviceIds) { - ToastStore.sharedInstance().addOrReplaceToast({ - key: toastKey(deviceId), - title: _t("New login. Was this you?"), - icon: "verification_warning", - props: { deviceId }, - component: sdk.getComponent("toasts.UnverifiedSessionToast"), - priority: 80, - }); + showUnverifiedSessionsToast(deviceId); } // ...and hide any we don't need any more for (const deviceId of this.displayingToastsForDeviceIds) { if (!newUnverifiedDeviceIds.has(deviceId)) { - ToastStore.sharedInstance().dismissToast(toastKey(deviceId)); + hideUnverifiedSessionsToast(deviceId); } } diff --git a/src/components/views/toasts/BulkUnverifiedSessionsToast.js b/src/components/views/toasts/BulkUnverifiedSessionsToast.js deleted file mode 100644 index 99ff529c35..0000000000 --- a/src/components/views/toasts/BulkUnverifiedSessionsToast.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2020 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 { _t } from '../../../languageHandler'; -import dis from "../../../dispatcher/dispatcher"; -import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import DeviceListener from '../../../DeviceListener'; -import FormButton from '../elements/FormButton'; -import { replaceableComponent } from '../../../utils/replaceableComponent'; - -@replaceableComponent("views.toasts.BulkUnverifiedSessionsToast") -export default class BulkUnverifiedSessionsToast extends React.PureComponent { - static propTypes = { - deviceIds: PropTypes.array, - } - - _onLaterClick = () => { - DeviceListener.sharedInstance().dismissUnverifiedSessions(this.props.deviceIds); - }; - - _onReviewClick = async () => { - DeviceListener.sharedInstance().dismissUnverifiedSessions(this.props.deviceIds); - - dis.dispatch({ - action: 'view_user_info', - userId: MatrixClientPeg.get().getUserId(), - }); - }; - - render() { - return (
    -
    - {_t("Verify all your sessions to ensure your account & messages are safe")} -
    -
    - - -
    -
    ); - } -} diff --git a/src/components/views/toasts/GenericToast.tsx b/src/components/views/toasts/GenericToast.tsx new file mode 100644 index 0000000000..9d69330857 --- /dev/null +++ b/src/components/views/toasts/GenericToast.tsx @@ -0,0 +1,42 @@ +/* +Copyright 2020 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, {ReactChild} from "react"; + +import FormButton from "../elements/FormButton"; + +interface IProps { + description: ReactChild; + acceptLabel: string; + rejectLabel?: string; + + onAccept(); + onReject?(); +} + +const GenericToast: React.FC = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => { + return
    +
    + { description } +
    +
    + {onReject && rejectLabel && } + +
    +
    ; +}; + +export default GenericToast; diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js deleted file mode 100644 index b5510e85b6..0000000000 --- a/src/components/views/toasts/SetupEncryptionToast.js +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2020 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 Modal from '../../../Modal'; -import * as sdk from "../../../index"; -import { _t } from '../../../languageHandler'; -import DeviceListener from '../../../DeviceListener'; -import SetupEncryptionDialog from "../dialogs/SetupEncryptionDialog"; -import { accessSecretStorage } from '../../../CrossSigningManager'; - -export default class SetupEncryptionToast extends React.PureComponent { - static propTypes = { - toastKey: PropTypes.string.isRequired, - kind: PropTypes.oneOf([ - 'set_up_encryption', - 'verify_this_session', - 'upgrade_encryption', - ]).isRequired, - }; - - _onLaterClick = () => { - DeviceListener.sharedInstance().dismissEncryptionSetup(); - }; - - _onSetupClick = async () => { - if (this.props.kind === "verify_this_session") { - Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog, - {}, null, /* priority = */ false, /* static = */ true); - } else { - const Spinner = sdk.getComponent("elements.Spinner"); - const modal = Modal.createDialog( - Spinner, null, 'mx_Dialog_spinner', /* priority */ false, /* static */ true, - ); - try { - await accessSecretStorage(); - } finally { - modal.close(); - } - } - }; - - getDescription() { - switch (this.props.kind) { - case 'set_up_encryption': - case 'upgrade_encryption': - return _t('Verify yourself & others to keep your chats safe'); - case 'verify_this_session': - return _t('Other users may not trust it'); - } - } - - getSetupCaption() { - switch (this.props.kind) { - case 'set_up_encryption': - return _t('Set up'); - case 'upgrade_encryption': - return _t('Upgrade'); - case 'verify_this_session': - return _t('Verify'); - } - } - - render() { - const FormButton = sdk.getComponent("elements.FormButton"); - return (
    -
    {this.getDescription()}
    -
    - - -
    -
    ); - } -} diff --git a/src/components/views/toasts/UnverifiedSessionToast.js b/src/components/views/toasts/UnverifiedSessionToast.js deleted file mode 100644 index 38cd9f20df..0000000000 --- a/src/components/views/toasts/UnverifiedSessionToast.js +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2020 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 { _t } from '../../../languageHandler'; -import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import Modal from '../../../Modal'; -import DeviceListener from '../../../DeviceListener'; -import NewSessionReviewDialog from '../dialogs/NewSessionReviewDialog'; -import FormButton from '../elements/FormButton'; -import { replaceableComponent } from '../../../utils/replaceableComponent'; - -@replaceableComponent("views.toasts.UnverifiedSessionToast") -export default class UnverifiedSessionToast extends React.PureComponent { - static propTypes = { - deviceId: PropTypes.string, - } - - _onLaterClick = () => { - DeviceListener.sharedInstance().dismissUnverifiedSessions([this.props.deviceId]); - }; - - _onReviewClick = async () => { - const cli = MatrixClientPeg.get(); - Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, { - userId: cli.getUserId(), - device: cli.getStoredDevice(cli.getUserId(), this.props.deviceId), - onFinished: (r) => { - if (!r) { - /* This'll come back false if the user clicks "this wasn't me" and saw a warning dialog */ - DeviceListener.sharedInstance().dismissUnverifiedSessions([this.props.deviceId]); - } - }, - }, null, /* priority = */ false, /* static = */ true); - }; - - render() { - const cli = MatrixClientPeg.get(); - const device = cli.getStoredDevice(cli.getUserId(), this.props.deviceId); - - return (
    -
    - {_t( - "Verify the new login accessing your account: %(name)s", { name: device.getDisplayName()})} -
    -
    - - -
    -
    ); - } -} diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.tsx similarity index 86% rename from src/components/views/toasts/VerificationRequestToast.js rename to src/components/views/toasts/VerificationRequestToast.tsx index 421dd7bea1..38e7e31989 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.tsx @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; + import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; @@ -24,8 +24,23 @@ import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver import dis from "../../../dispatcher/dispatcher"; import ToastStore from "../../../stores/ToastStore"; import Modal from "../../../Modal"; +import GenericToast from "./GenericToast"; +import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo"; + +interface IProps { + toastKey: string; + request: VerificationRequest; +} + +interface IState { + counter: number; + device?: DeviceInfo; +} + +export default class VerificationRequestToast extends React.PureComponent { + private intervalHandle: NodeJS.Timeout; -export default class VerificationRequestToast extends React.PureComponent { constructor(props) { super(props); this.state = {counter: Math.ceil(props.request.timeout / 1000)}; @@ -34,7 +49,7 @@ export default class VerificationRequestToast extends React.PureComponent { async componentDidMount() { const {request} = this.props; if (request.timeout && request.timeout > 0) { - this._intervalHandle = setInterval(() => { + this.intervalHandle = setInterval(() => { let {counter} = this.state; counter = Math.max(0, counter - 1); this.setState({counter}); @@ -56,7 +71,7 @@ export default class VerificationRequestToast extends React.PureComponent { } componentWillUnmount() { - clearInterval(this._intervalHandle); + clearInterval(this.intervalHandle); const {request} = this.props; request.off("change", this._checkRequestIsPending); } @@ -110,7 +125,6 @@ export default class VerificationRequestToast extends React.PureComponent { }; render() { - const FormButton = sdk.getComponent("elements.FormButton"); const {request} = this.props; let nameLabel; if (request.isSelfVerification) { @@ -133,20 +147,16 @@ export default class VerificationRequestToast extends React.PureComponent { } } } - const declineLabel = this.state.counter == 0 ? + const declineLabel = this.state.counter === 0 ? _t("Decline") : _t("Decline (%(counter)s)", {counter: this.state.counter}); - return (
    -
    {nameLabel}
    -
    - - -
    -
    ); + + return ; } } - -VerificationRequestToast.propTypes = { - request: PropTypes.object.isRequired, - toastKey: PropTypes.string.isRequired, -}; diff --git a/src/toasts/BulkUnverifiedSessionsToast.ts b/src/toasts/BulkUnverifiedSessionsToast.ts new file mode 100644 index 0000000000..41717e0804 --- /dev/null +++ b/src/toasts/BulkUnverifiedSessionsToast.ts @@ -0,0 +1,58 @@ +/* +Copyright 2020 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 { _t } from '../languageHandler'; +import dis from "../dispatcher/dispatcher"; +import { MatrixClientPeg } from '../MatrixClientPeg'; +import DeviceListener from '../DeviceListener'; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const TOAST_KEY = "reviewsessions"; + +export const showToast = (deviceIds: Set) => { + const onAccept = () => { + DeviceListener.sharedInstance().dismissUnverifiedSessions(deviceIds); + + dis.dispatch({ + action: 'view_user_info', + userId: MatrixClientPeg.get().getUserId(), + }); + }; + + const onReject = () => { + DeviceListener.sharedInstance().dismissUnverifiedSessions(deviceIds); + }; + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Review where you’re logged in"), + icon: "verification_warning", + props: { + description: _t("Verify all your sessions to ensure your account & messages are safe"), + acceptLabel: _t("Review"), + onAccept, + rejectLabel: _t("Later"), + onReject, + }, + component: GenericToast, + priority: 50, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; diff --git a/src/toasts/SetupEncryptionToast.ts b/src/toasts/SetupEncryptionToast.ts new file mode 100644 index 0000000000..d35bbf1c88 --- /dev/null +++ b/src/toasts/SetupEncryptionToast.ts @@ -0,0 +1,106 @@ +/* +Copyright 2020 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 Modal from "../Modal"; +import * as sdk from "../index"; +import { _t } from "../languageHandler"; +import DeviceListener from "../DeviceListener"; +import SetupEncryptionDialog from "../components/views/dialogs/SetupEncryptionDialog"; +import { accessSecretStorage } from "../CrossSigningManager"; +import ToastStore from "../stores/ToastStore"; +import GenericToast from "../components/views/toasts/GenericToast"; + +const TOAST_KEY = "setupencryption"; + +const getTitle = (kind: Kind) => { + switch (kind) { + case Kind.SET_UP_ENCRYPTION: + return _t("Set up encryption"); + case Kind.UPGRADE_ENCRYPTION: + return _t("Encryption upgrade available"); + case Kind.VERIFY_THIS_SESSION: + return _t("Verify this session"); + } +}; + +const getSetupCaption = (kind: Kind) => { + switch (kind) { + case Kind.SET_UP_ENCRYPTION: + return _t("Set up"); + case Kind.UPGRADE_ENCRYPTION: + return _t("Upgrade"); + case Kind.VERIFY_THIS_SESSION: + return _t("Verify"); + } +}; + +const getDescription = (kind: Kind) => { + switch (kind) { + case Kind.SET_UP_ENCRYPTION: + case Kind.UPGRADE_ENCRYPTION: + return _t("Verify yourself & others to keep your chats safe"); + case Kind.VERIFY_THIS_SESSION: + return _t("Other users may not trust it"); + } +}; + +export enum Kind { + SET_UP_ENCRYPTION = "set_up_encryption", + UPGRADE_ENCRYPTION = "upgrade_encryption", + VERIFY_THIS_SESSION = "verify_this_session", +} + +const onReject = () => { + DeviceListener.sharedInstance().dismissEncryptionSetup(); +}; + +export const showToast = (kind: Kind) => { + const onAccept = async () => { + if (kind === Kind.VERIFY_THIS_SESSION) { + Modal.createTrackedDialog("Verify session", "Verify session", SetupEncryptionDialog, + {}, null, /* priority = */ false, /* static = */ true); + } else { + const Spinner = sdk.getComponent("elements.Spinner"); + const modal = Modal.createDialog( + Spinner, null, "mx_Dialog_spinner", /* priority */ false, /* static */ true, + ); + try { + await accessSecretStorage(); + } finally { + modal.close(); + } + } + }; + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: getTitle(kind), + icon: "verification_warning", + props: { + description: getDescription(kind), + acceptLabel: getSetupCaption(kind), + onAccept, + rejectLabel: _t("Later"), + onReject, + }, + component: GenericToast, + priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; diff --git a/src/toasts/UnverifiedSessionToast.ts b/src/toasts/UnverifiedSessionToast.ts new file mode 100644 index 0000000000..635356b9db --- /dev/null +++ b/src/toasts/UnverifiedSessionToast.ts @@ -0,0 +1,70 @@ +/* +Copyright 2020 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 { _t } from '../languageHandler'; +import { MatrixClientPeg } from '../MatrixClientPeg'; +import Modal from '../Modal'; +import DeviceListener from '../DeviceListener'; +import NewSessionReviewDialog from '../components/views/dialogs/NewSessionReviewDialog'; +import ToastStore from "../stores/ToastStore"; +import GenericToast from "../components/views/toasts/GenericToast"; + +function toastKey(deviceId: string) { + return "unverified_session_" + deviceId; +} + +export const showToast = (deviceId: string) => { + const cli = MatrixClientPeg.get(); + + const onAccept = () => { + Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, { + userId: cli.getUserId(), + device: cli.getStoredDevice(cli.getUserId(), deviceId), + onFinished: (r) => { + if (!r) { + /* This'll come back false if the user clicks "this wasn't me" and saw a warning dialog */ + DeviceListener.sharedInstance().dismissUnverifiedSessions([deviceId]); + } + }, + }, null, /* priority = */ false, /* static = */ true); + }; + + const onReject = () => { + DeviceListener.sharedInstance().dismissUnverifiedSessions([deviceId]); + }; + + const device = cli.getStoredDevice(cli.getUserId(), deviceId); + + ToastStore.sharedInstance().addOrReplaceToast({ + key: toastKey(deviceId), + title: _t("New login. Was this you?"), + icon: "verification_warning", + props: { + description: _t( + "Verify the new login accessing your account: %(name)s", { name: device.getDisplayName()}), + acceptLabel: _t("Verify"), + onAccept, + rejectLabel: _t("Later"), + onReject, + }, + component: GenericToast, + priority: 80, + }); +}; + +export const hideToast = (deviceId: string) => { + ToastStore.sharedInstance().dismissToast(deviceId); +}; From a822ab49d5e9dbc2ead9d3a828b211bce3d4e053 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 13:40:30 +0100 Subject: [PATCH 252/399] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 96ccf1589d..8ac05bf429 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -102,11 +102,6 @@ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", - "Verify this session": "Verify this session", - "Encryption upgrade available": "Encryption upgrade available", - "Set up encryption": "Set up encryption", - "Review where you’re logged in": "Review where you’re logged in", - "New login. Was this you?": "New login. Was this you?", "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", @@ -396,6 +391,20 @@ "Common names and surnames are easy to guess": "Common names and surnames are easy to guess", "Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess", "Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess", + "Review where you’re logged in": "Review where you’re logged in", + "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", + "Review": "Review", + "Later": "Later", + "Set up encryption": "Set up encryption", + "Encryption upgrade available": "Encryption upgrade available", + "Verify this session": "Verify this session", + "Set up": "Set up", + "Upgrade": "Upgrade", + "Verify": "Verify", + "Verify yourself & others to keep your chats safe": "Verify yourself & others to keep your chats safe", + "Other users may not trust it": "Other users may not trust it", + "New login. Was this you?": "New login. Was this you?", + "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", "There was an error joining the room": "There was an error joining the room", "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", @@ -570,15 +579,6 @@ "Headphones": "Headphones", "Folder": "Folder", "Pin": "Pin", - "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", - "Later": "Later", - "Review": "Review", - "Verify yourself & others to keep your chats safe": "Verify yourself & others to keep your chats safe", - "Other users may not trust it": "Other users may not trust it", - "Set up": "Set up", - "Upgrade": "Upgrade", - "Verify": "Verify", - "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", "From %(deviceName)s (%(deviceId)s)": "From %(deviceName)s (%(deviceId)s)", "Decline (%(counter)s)": "Decline (%(counter)s)", "Accept to continue:": "Accept to continue:", From ac0d794877f4f2d2634087cfa57eecf1044ca83d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 13:49:56 +0100 Subject: [PATCH 253/399] Fix: Tag_DM is not defined Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 1e5002419d..e4290f87d9 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -44,6 +44,7 @@ import {RoomListStoreTempProxy} from "../../../stores/room-list/RoomListStoreTem import {DefaultTagID} from "../../../stores/room-list/models"; import * as Unread from "../../../Unread"; import RoomViewStore from "../../../stores/RoomViewStore"; +import {TAG_DM} from "../../../stores/RoomListStore"; const HIDE_CONFERENCE_CHANS = true; const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/; From 762918d664e4b5ab6ac2e7e36eabb0b26b75e6a6 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Fri, 22 May 2020 14:00:29 +0100 Subject: [PATCH 254/399] Prepare changelog for v2.6.1 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7901062b89..e5515f1015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +Changes in [2.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.1) (2020-05-22) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.6.0...v2.6.1) + + * Fix key backup restore with SSSS + [\#4617](https://github.com/matrix-org/matrix-react-sdk/pull/4617) + * Remove SSSS key upgrade check from rageshake + [\#4616](https://github.com/matrix-org/matrix-react-sdk/pull/4616) + Changes in [2.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.6.0) (2020-05-19) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.6.0-rc.1...v2.6.0) From 5a4da13e863ed554323e00fc1783eaaa776c923e Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Fri, 22 May 2020 14:00:29 +0100 Subject: [PATCH 255/399] v2.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 28f3d1633c..6ceef02f24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.6.0", + "version": "2.6.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From af59d3a09423153056864ebd16eb10626a7d2ab7 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Fri, 22 May 2020 14:14:09 +0100 Subject: [PATCH 256/399] Reset matrix-js-sdk back to develop branch --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 6cdc771c5b..a97c093b87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5760,7 +5760,7 @@ mathml-tag-names@^2.0.1: "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "6.1.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e3c6a0e1a08a3812ba988e60eb5a2a013bb27404" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/a4a7097c103da42075f2c70e070fd01fa6fb0d48" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From c464abaa498df44ea6d0366c64eec6be7afa4556 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 14:28:01 +0100 Subject: [PATCH 257/399] Iterate toast count indicator more logically Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/ToastContainer.tsx | 14 +++++++++++--- src/stores/ToastStore.ts | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/components/structures/ToastContainer.tsx b/src/components/structures/ToastContainer.tsx index 9440aa3463..c7b8e911d3 100644 --- a/src/components/structures/ToastContainer.tsx +++ b/src/components/structures/ToastContainer.tsx @@ -15,12 +15,12 @@ limitations under the License. */ import * as React from "react"; -import { _t } from '../../languageHandler'; import ToastStore, {IToast} from "../../stores/ToastStore"; import classNames from "classnames"; interface IState { toasts: IToast[]; + countSeen: number; } export default class ToastContainer extends React.Component<{}, IState> { @@ -28,6 +28,7 @@ export default class ToastContainer extends React.Component<{}, IState> { super(props, context); this.state = { toasts: ToastStore.sharedInstance().getToasts(), + countSeen: 0, }; // Start listening here rather than in componentDidMount because @@ -42,7 +43,10 @@ export default class ToastContainer extends React.Component<{}, IState> { } _onToastStoreUpdate = () => { - this.setState({toasts: ToastStore.sharedInstance().getToasts()}); + this.setState({ + toasts: ToastStore.sharedInstance().getToasts(), + countSeen: ToastStore.sharedInstance().getCountSeen(), + }); }; render() { @@ -56,7 +60,11 @@ export default class ToastContainer extends React.Component<{}, IState> { "mx_Toast_hasIcon": icon, [`mx_Toast_icon_${icon}`]: icon, }); - const countIndicator = isStacked ? _t(" (1/%(totalCount)s)", {totalCount}) : null; + + let countIndicator; + if (isStacked || this.state.countSeen > 0) { + countIndicator = ` (${this.state.countSeen + 1}/${this.state.countSeen + totalCount})`; + } const toastProps = Object.assign({}, props, { key, diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 4f6d2963c5..b6b6f19872 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -32,6 +32,9 @@ export interface IToast[] = []; + // The count of toasts which have been seen & dealt with in this stack + // where the count resets when the stack of toasts clears. + private countSeen: number = 0; static sharedInstance() { if (!window.mx_ToastStore) window.mx_ToastStore = new ToastStore(); @@ -40,6 +43,7 @@ export default class ToastStore extends EventEmitter { reset() { this.toasts = []; + this.countSeen = 0; } /** @@ -67,6 +71,12 @@ export default class ToastStore extends EventEmitter { const length = this.toasts.length; this.toasts = this.toasts.filter(t => t.key !== key); if (length !== this.toasts.length) { + if (this.toasts.length === 0) { + this.countSeen = 0; + } else { + this.countSeen++; + } + this.emit('update'); } } @@ -74,4 +84,8 @@ export default class ToastStore extends EventEmitter { getToasts() { return this.toasts; } + + getCountSeen() { + return this.countSeen; + } } From 4e67e46863877a50e040dced1447970daa967cc5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 14:29:30 +0100 Subject: [PATCH 258/399] fix countSeen Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/ToastContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ToastContainer.tsx b/src/components/structures/ToastContainer.tsx index c7b8e911d3..c88c830d59 100644 --- a/src/components/structures/ToastContainer.tsx +++ b/src/components/structures/ToastContainer.tsx @@ -28,7 +28,7 @@ export default class ToastContainer extends React.Component<{}, IState> { super(props, context); this.state = { toasts: ToastStore.sharedInstance().getToasts(), - countSeen: 0, + countSeen: ToastStore.sharedInstance().getCountSeen(), }; // Start listening here rather than in componentDidMount because From 49c0748990b00be04520bfb85b2a79589b32cb34 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 14:32:41 +0100 Subject: [PATCH 259/399] delint and i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 1 - src/stores/ToastStore.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8ac05bf429..812ce826fc 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2083,7 +2083,6 @@ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "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", - " (1/%(totalCount)s)": " (1/%(totalCount)s)", "Guest": "Guest", "Your profile": "Your profile", "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index b6b6f19872..89b4dc2dc1 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -34,7 +34,7 @@ export default class ToastStore extends EventEmitter { private toasts: IToast[] = []; // The count of toasts which have been seen & dealt with in this stack // where the count resets when the stack of toasts clears. - private countSeen: number = 0; + private countSeen = 0; static sharedInstance() { if (!window.mx_ToastStore) window.mx_ToastStore = new ToastStore(); From 8a1ace43dac126f146b7fd468a746dca00135457 Mon Sep 17 00:00:00 2001 From: XoseM Date: Fri, 22 May 2020 13:52:01 +0000 Subject: [PATCH 260/399] Translated using Weblate (Galician) Currently translated at 44.4% (1030 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 48eb1338b4..b297f23fd6 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -1026,5 +1026,20 @@ "Session already verified!": "A sesión xa está verificada!", "WARNING: Session already verified, but keys do NOT MATCH!": "AVISO: xa está verificada a sesión, pero as chaves NON CONCORDAN!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "AVISO: FALLOU A VERIFICACIÓN DAS CHAVES! A chave de firma para %(userId)s na sesión %(deviceId)s é \"%(fprint)s\" que non concordan coa chave proporcionada \"%(fingerprint)s\". Esto podería significar que as túas comunicacións foron interceptadas!", - "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "A chave de firma proporcionada concorda coa chave de firma recibida desde a sesión %(deviceId)s de %(userId)s. Sesión marcada como verificada." + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "A chave de firma proporcionada concorda coa chave de firma recibida desde a sesión %(deviceId)s de %(userId)s. Sesión marcada como verificada.", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se usas ou non a función 'breadcrumbs ' (avatares enriba da listaxe de salas)", + "Unbans user with given ID": "Desbloquea usuaria co ID dado", + "Verifies a user, session, and pubkey tuple": "Verifica unha usuaria, sesión e chave pública", + "Forces the current outbound group session in an encrypted room to be discarded": "Forza que se descarte a sesión de saída actual nunha sala cifrada", + "Sends the given message coloured as a rainbow": "Envía a mensaxe dada colorida como o arco da vella", + "Sends the given emote coloured as a rainbow": "Envía o emoji colorido como un arco da vella", + "Displays list of commands with usages and descriptions": "Mostra unha listaxe de comandos con usos e descricións", + "Displays information about a user": "Mostra información acerca da usuaria", + "Send a bug report with logs": "Envía un informe de fallos con rexistros", + "Opens chat with the given user": "Abre unha conversa coa usuaria", + "Sends a message to the given user": "Envía unha mensaxe a usuaria", + "%(senderName)s made no change.": "%(senderName)s non fixo cambios.", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s cambiou o nome da sala de %(oldRoomName)s a %(newRoomName)s.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s actualizou esta sala.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s converteu en pública a sala para calquera que teña a ligazón." } From 24c379119644ece7dce72e1618cb4bdc45da7b31 Mon Sep 17 00:00:00 2001 From: XoseM Date: Fri, 22 May 2020 13:57:33 +0000 Subject: [PATCH 261/399] Translated using Weblate (Galician) Currently translated at 44.4% (1030 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index b297f23fd6..5b7dd05251 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -1041,5 +1041,6 @@ "%(senderName)s made no change.": "%(senderName)s non fixo cambios.", "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s cambiou o nome da sala de %(oldRoomName)s a %(newRoomName)s.", "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s actualizou esta sala.", - "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s converteu en pública a sala para calquera que teña a ligazón." + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s converteu en pública a sala para calquera que teña a ligazón.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s fixo que a sala sexa só por convite." } From efb3dd2e25a390dfaba3825bb440b6daa18a85bd Mon Sep 17 00:00:00 2001 From: MamasLT Date: Fri, 22 May 2020 13:42:27 +0000 Subject: [PATCH 262/399] Translated using Weblate (Lithuanian) Currently translated at 48.7% (1131 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index c6f3abef0f..de8109913c 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1161,5 +1161,9 @@ "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų sesijų ir nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, prisijunkite iš naujo kiekviename įrenginyje.", "Show more": "Rodyti daugiau", "Log in to your new account.": "Prisijunkite į savo naują paskyrą.", - "Registration Successful": "Registracija sėkminga" + "Registration Successful": "Registracija sėkminga", + "Welcome to %(appName)s": "Sveiki prisijungę į %(appName)s", + "Liberate your communication": "Išlaisvinkite savo bendravimą", + "Explore Public Rooms": "Žvalgyti viešus kambarius", + "Create a Group Chat": "Sukurti grupės pokalbį" } From 08fadb092c930cc25323bba29ec8f5979f4dd9e2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:43:15 +0100 Subject: [PATCH 263/399] Remove redundant component Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../settings/EnableNotificationsButton.js | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 src/components/views/settings/EnableNotificationsButton.js diff --git a/src/components/views/settings/EnableNotificationsButton.js b/src/components/views/settings/EnableNotificationsButton.js deleted file mode 100644 index e4b348dfbd..0000000000 --- a/src/components/views/settings/EnableNotificationsButton.js +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React from "react"; -import createReactClass from 'create-react-class'; -import Notifier from "../../../Notifier"; -import dis from "../../../dispatcher/dispatcher"; -import { _t } from '../../../languageHandler'; - -export default createReactClass({ - displayName: 'EnableNotificationsButton', - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - }, - - componentWillUnmount: function() { - dis.unregister(this.dispatcherRef); - }, - - onAction: function(payload) { - if (payload.action !== "notifier_enabled") { - return; - } - this.forceUpdate(); - }, - - enabled: function() { - return Notifier.isEnabled(); - }, - - onClick: function() { - const self = this; - if (!Notifier.supportsDesktopNotifications()) { - return; - } - if (!Notifier.isEnabled()) { - Notifier.setEnabled(true, function() { - self.forceUpdate(); - }); - } else { - Notifier.setEnabled(false); - } - this.forceUpdate(); - }, - - render: function() { - if (this.enabled()) { - return ( - - ); - } else { - return ( - - ); - } - }, -}); From a977b8c4ca12570919a1cf5deceaf71652648510 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:55:43 +0100 Subject: [PATCH 264/399] Fix lifecycle to reset things before it starts them Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Lifecycle.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 22c5d48317..5fb54ede7f 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -575,10 +575,12 @@ async function startMatrixClient(startSyncing=true) { // to work). dis.dispatch({action: 'will_start_client'}, true); + // reset things first just in case + TypingStore.sharedInstance().reset(); + ToastStore.sharedInstance().reset(); + Notifier.start(); UserActivity.sharedInstance().start(); - TypingStore.sharedInstance().reset(); // just in case - ToastStore.sharedInstance().reset(); DMRoomMap.makeShared().start(); IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.start(); From 3732d1f5a5d0ad2fe22c73db924d612651967c79 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:56:25 +0100 Subject: [PATCH 265/399] Migrate Desktop Notifications MatrixToolbar to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 15 +++--- src/components/structures/LoggedInView.tsx | 7 +-- src/components/structures/MatrixChat.tsx | 6 --- src/components/views/globals/MatrixToolbar.js | 45 ----------------- src/toasts/DesktopNotificationsToast.ts | 50 +++++++++++++++++++ 5 files changed, 60 insertions(+), 63 deletions(-) delete mode 100644 src/components/views/globals/MatrixToolbar.js create mode 100644 src/toasts/DesktopNotificationsToast.ts diff --git a/src/Notifier.js b/src/Notifier.js index 2ffa92452b..cc804904e2 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -26,6 +26,10 @@ import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import { + showToast as showNotificationsToast, + hideToast as hideNotificationsToast, +} from "./toasts/DesktopNotificationsToast"; /* * Dispatches: @@ -184,6 +188,10 @@ const Notifier = { MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange); this.toolbarHidden = false; this.isSyncing = false; + + if (this.shouldShowToolbar()) { + showNotificationsToast(); + } }, stop: function() { @@ -278,12 +286,7 @@ const Notifier = { Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden); - // XXX: why are we dispatching this here? - // this is nothing to do with notifier_enabled - dis.dispatch({ - action: "notifier_enabled", - value: this.isEnabled(), - }); + hideNotificationsToast(); // update the info to localStorage for persistent settings if (persistent && global.localStorage) { diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 148d10fe8d..0d3eda759e 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -68,7 +68,6 @@ interface IProps { showCookieBar: boolean; hasNewVersion: boolean; userHasGeneratedPassword: boolean; - showNotifierToolbar: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -184,8 +183,7 @@ class LoggedInView extends React.PureComponent { if ( (prevProps.showCookieBar !== this.props.showCookieBar) || (prevProps.hasNewVersion !== this.props.hasNewVersion) || - (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) || - (prevProps.showNotifierToolbar !== this.props.showNotifierToolbar) + (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -599,7 +597,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); const CookieBar = sdk.getComponent('globals.CookieBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); @@ -680,8 +677,6 @@ class LoggedInView extends React.PureComponent { topBar = ; } else if (this.state.userHasGeneratedPassword) { topBar = ; - } else if (this.props.showNotifierToolbar) { - topBar = ; } let bodyClasses = 'mx_MatrixChat'; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index e6a56c683f..120497e5ef 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -184,7 +184,6 @@ interface IState { hideToSRUsers: boolean; syncError?: Error; resizeNotifier: ResizeNotifier; - showNotifierToolbar: boolean; serverConfig?: ValidatedServerConfig; ready: boolean; thirdPartyInvite?: object; @@ -238,7 +237,6 @@ export default class MatrixChat extends React.PureComponent { syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. resizeNotifier: new ResizeNotifier(), - showNotifierToolbar: false, ready: false, }; @@ -686,9 +684,6 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'view_my_groups'}); } break; - case 'notifier_enabled': - this.setState({showNotifierToolbar: Notifier.shouldShowToolbar()}); - break; case 'hide_left_panel': this.setState({ collapseLhs: true, @@ -1381,7 +1376,6 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'focus_composer'}); this.setState({ ready: true, - showNotifierToolbar: Notifier.shouldShowToolbar(), }); }); cli.on('Call.incoming', function(call) { diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js deleted file mode 100644 index 758e4d62aa..0000000000 --- a/src/components/views/globals/MatrixToolbar.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React from 'react'; -import createReactClass from 'create-react-class'; -import { _t } from '../../../languageHandler'; -import Notifier from '../../../Notifier'; -import AccessibleButton from '../../../components/views/elements/AccessibleButton'; - -export default createReactClass({ - displayName: 'MatrixToolbar', - - hideToolbar: function() { - Notifier.setToolbarHidden(true); - }, - - onClick: function() { - Notifier.setEnabled(true); - }, - - render: function() { - return ( -
    - -
    - { _t('You are not receiving desktop notifications') } { _t('Enable them now') } -
    - {_t('Close')} -
    - ); - }, -}); diff --git a/src/toasts/DesktopNotificationsToast.ts b/src/toasts/DesktopNotificationsToast.ts new file mode 100644 index 0000000000..02f0730759 --- /dev/null +++ b/src/toasts/DesktopNotificationsToast.ts @@ -0,0 +1,50 @@ +/* +Copyright 2020 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 { _t } from "../languageHandler"; +import Notifier from "../Notifier"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + Notifier.setEnabled(true); +}; + +const onReject = () => { + Notifier.setToolbarHidden(true); +}; + +const TOAST_KEY = "desktopnotifications"; + +export const showToast = () => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: _t("You are not receiving desktop notifications"), + acceptLabel: _t("Enable them now"), + onAccept, + rejectLabel: _t("Close"), + onReject, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 89292ca47be3fc0a714024a708452452e9938ee6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:03:16 +0100 Subject: [PATCH 266/399] Fix toast priority sorting to put the highest priority into slot[0] Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 4f6d2963c5..23317a0ad3 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -55,7 +55,7 @@ export default class ToastStore extends EventEmitter { const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; - while (newIndex > 0 && this.toasts[newIndex - 1].priority > newToast.priority) --newIndex; + while (newIndex > 0 && this.toasts[newIndex - 1].priority < newToast.priority) --newIndex; this.toasts.splice(newIndex, 0, newToast); } else { this.toasts[oldIndex] = newToast; From c91f8c2631799d92cc582dbe6eebcdec3e0a2cb4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:04:21 +0100 Subject: [PATCH 267/399] Migrate Analytics Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 9 -- src/components/structures/MatrixChat.tsx | 28 ++---- src/components/views/globals/CookieBar.js | 103 --------------------- src/i18n/strings/en_EN.json | 14 +-- src/toasts/AnalyticsToast.tsx | 75 +++++++++++++++ 5 files changed, 92 insertions(+), 137 deletions(-) delete mode 100644 src/components/views/globals/CookieBar.js create mode 100644 src/toasts/AnalyticsToast.tsx diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 0d3eda759e..88b64f0cbc 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -65,7 +65,6 @@ interface IProps { initialEventPixelOffset: number; leftDisabled: boolean; rightDisabled: boolean; - showCookieBar: boolean; hasNewVersion: boolean; userHasGeneratedPassword: boolean; page_type: string; @@ -181,7 +180,6 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.showCookieBar !== this.props.showCookieBar) || (prevProps.hasNewVersion !== this.props.hasNewVersion) || (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) ) { @@ -597,7 +595,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const CookieBar = sdk.getComponent('globals.CookieBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); @@ -663,12 +660,6 @@ class LoggedInView extends React.PureComponent { adminContact={usageLimitEvent.getContent().admin_contact} limitType={usageLimitEvent.getContent().limit_type} />; - } else if (this.props.showCookieBar && - this.props.config.piwik && - navigator.doNotTrack !== "1" - ) { - const policyUrl = this.props.config.piwik.policyUrl || null; - topBar = ; } else if (this.props.hasNewVersion) { topBar = { newVersionReleaseNotes: null, checkingForUpdate: null, - showCookieBar: false, - hideToSRUsers: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. @@ -337,12 +338,6 @@ export default class MatrixChat extends React.PureComponent { }); } - if (SettingsStore.getValue("showCookieBar")) { - this.setState({ - showCookieBar: true, - }); - } - if (SettingsStore.getValue("analyticsOptIn")) { Analytics.enable(); } @@ -756,19 +751,13 @@ export default class MatrixChat extends React.PureComponent { case 'accept_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, true); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); Analytics.enable(); break; case 'reject_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, false); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); break; } }; @@ -1246,6 +1235,10 @@ export default class MatrixChat extends React.PureComponent { } StorageManager.tryPersistStorage(); + + if (SettingsStore.getValue("showCookieBar") && this.props.config.piwik && navigator.doNotTrack !== "1") { + showAnalyticsToast(this.props.config.piwik && this.props.config.piwik.policyUrl); + } } private showScreenAfterLogin() { @@ -2031,7 +2024,6 @@ export default class MatrixChat extends React.PureComponent { onCloseAllSettings={this.onCloseAllSettings} onRegistered={this.onRegistered} currentRoomId={this.state.currentRoomId} - showCookieBar={this.state.showCookieBar} /> ); } else { diff --git a/src/components/views/globals/CookieBar.js b/src/components/views/globals/CookieBar.js deleted file mode 100644 index bf264686d0..0000000000 --- a/src/components/views/globals/CookieBar.js +++ /dev/null @@ -1,103 +0,0 @@ -/* -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 React from 'react'; -import PropTypes from 'prop-types'; -import dis from '../../../dispatcher/dispatcher'; -import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; -import Analytics from '../../../Analytics'; - -export default class CookieBar extends React.Component { - static propTypes = { - policyUrl: PropTypes.string, - } - - constructor() { - super(); - } - - onUsageDataClicked(e) { - e.stopPropagation(); - e.preventDefault(); - Analytics.showDetailsModal(); - } - - onAccept() { - dis.dispatch({ - action: 'accept_cookies', - }); - } - - onReject() { - dis.dispatch({ - action: 'reject_cookies', - }); - } - - render() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const toolbarClasses = "mx_MatrixToolbar"; - return ( -
    - -
    - { this.props.policyUrl ? _t( - "Please help improve Riot.im by sending anonymous usage data. " + - "This will use a cookie " + - "(please see our Cookie Policy).", - {}, - { - 'UsageDataLink': (sub) => - { sub } - , - // XXX: We need to link to the page that explains our cookies - 'PolicyLink': (sub) => - { sub } - - , - }, - ) : _t( - "Please help improve Riot.im by sending anonymous usage data. " + - "This will use a cookie.", - {}, - { - 'UsageDataLink': (sub) => - { sub } - , - }, - ) } -
    - - { _t("Yes, I want to help!") } - - - {_t('Close')} - -
    - ); - } -} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8ac05bf429..a79ee97109 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -391,10 +391,17 @@ "Common names and surnames are easy to guess": "Common names and surnames are easy to guess", "Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess", "Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess", + "Notifications": "Notifications", + "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Send anonymous usage data which helps us improve Riot. This will use a cookie.", + "I want to help": "I want to help", + "No": "No", "Review where you’re logged in": "Review where you’re logged in", "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", "Review": "Review", "Later": "Later", + "You are not receiving desktop notifications": "You are not receiving desktop notifications", + "Enable them now": "Enable them now", + "Close": "Close", "Set up encryption": "Set up encryption", "Encryption upgrade available": "Encryption upgrade available", "Verify this session": "Verify this session", @@ -643,8 +650,6 @@ "Last seen": "Last seen", "Failed to set display name": "Failed to set display name", "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", - "Disable Notifications": "Disable Notifications", - "Enable Notifications": "Enable Notifications", "Securely cache encrypted messages locally for them to appear in search results, using ": "Securely cache encrypted messages locally for them to appear in search results, using ", " to store messages from ": " to store messages from ", "rooms.": "rooms.", @@ -815,7 +820,6 @@ "Ban list rules - %(roomName)s": "Ban list rules - %(roomName)s", "Server rules": "Server rules", "User rules": "User rules", - "Close": "Close", "You have not ignored anyone.": "You have not ignored anyone.", "You are currently ignoring:": "You are currently ignoring:", "You are not subscribed to any lists": "You are not subscribed to any lists", @@ -836,7 +840,6 @@ "If this isn't what you want, please use a different tool to ignore users.": "If this isn't what you want, please use a different tool to ignore users.", "Room ID or alias of ban list": "Room ID or alias of ban list", "Subscribe": "Subscribe", - "Notifications": "Notifications", "Start automatically after system login": "Start automatically after system login", "Always show the window menu bar": "Always show the window menu bar", "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close", @@ -1287,7 +1290,6 @@ "Verify by emoji": "Verify by emoji", "Almost there! Is your other session showing the same shield?": "Almost there! Is your other session showing the same shield?", "Almost there! Is %(displayName)s showing the same shield?": "Almost there! Is %(displayName)s showing the same shield?", - "No": "No", "Yes": "Yes", "Verify all users in a room to ensure it's secure.": "Verify all users in a room to ensure it's secure.", "In encrypted rooms, verify all users to ensure it’s secure.": "In encrypted rooms, verify all users to ensure it’s secure.", @@ -1384,8 +1386,6 @@ "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).", "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.", "Yes, I want to help!": "Yes, I want to help!", - "You are not receiving desktop notifications": "You are not receiving desktop notifications", - "Enable them now": "Enable them now", "What's New": "What's New", "Update": "Update", "What's new?": "What's new?", diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx new file mode 100644 index 0000000000..5b53a903fe --- /dev/null +++ b/src/toasts/AnalyticsToast.tsx @@ -0,0 +1,75 @@ +/* +Copyright 2020 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 { _t } from "../languageHandler"; +import dis from "../dispatcher/dispatcher"; +import Analytics from "../Analytics"; +import AccessibleButton from "../components/views/elements/AccessibleButton"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + dis.dispatch({ + action: 'accept_cookies', + }); +}; + +const onReject = () => { + dis.dispatch({ + action: "reject_cookies", + }); +}; + +const onUsageDataClicked = () => { + Analytics.showDetailsModal(); +}; + +const TOAST_KEY = "analytics"; + +export const showToast = (policyUrl?: string) => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: _t( + "Send anonymous usage data which helps us improve Riot. " + + "This will use a cookie.", + {}, + { + "UsageDataLink": (sub) => ( + { sub } + ), + // XXX: We need to link to the page that explains our cookies + "PolicyLink": (sub) => policyUrl ? ( + { sub } + ) : sub, + }, + ), + acceptLabel: _t("I want to help"), + onAccept, + rejectLabel: _t("No"), + onReject, + }, + component: GenericToast, + priority: 10, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 5d0040b8b36d1474db807748d1191844b424a569 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:15:22 +0100 Subject: [PATCH 268/399] Migrate Password Nag Bar Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 22 ++++---- .../views/globals/PasswordNagBar.js | 53 ------------------- src/toasts/SetPasswordToast.ts | 47 ++++++++++++++++ 3 files changed, 58 insertions(+), 64 deletions(-) delete mode 100644 src/components/views/globals/PasswordNagBar.js create mode 100644 src/toasts/SetPasswordToast.ts diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 88b64f0cbc..131e6a3867 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -43,6 +43,11 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import PlatformPeg from "../../PlatformPeg"; import { RoomListStoreTempProxy } from "../../stores/room-list/RoomListStoreTempProxy"; import { DefaultTagID } from "../../stores/room-list/models"; +import { + showToast as showSetPasswordToast, + hideToast as hideSetPasswordToast +} from "../../toasts/SetPasswordToast"; + // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. // NB. this is just for server notices rather than pinned messages in general. @@ -66,7 +71,6 @@ interface IProps { leftDisabled: boolean; rightDisabled: boolean; hasNewVersion: boolean; - userHasGeneratedPassword: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -96,7 +100,6 @@ interface IState { syncErrorData: any; useCompactLayout: boolean; serverNoticeEvents: MatrixEvent[]; - userHasGeneratedPassword: boolean; } /** @@ -139,7 +142,6 @@ class LoggedInView extends React.PureComponent { this.state = { mouseDown: undefined, syncErrorData: undefined, - userHasGeneratedPassword: false, // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), // any currently active server notice events @@ -180,8 +182,7 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.hasNewVersion !== this.props.hasNewVersion) || - (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) + (prevProps.hasNewVersion !== this.props.hasNewVersion) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -216,9 +217,11 @@ class LoggedInView extends React.PureComponent { }; _setStateFromSessionStore = () => { - this.setState({ - userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()), - }); + if (this._sessionStore.getCachedPassword()) { + showSetPasswordToast(); + } else { + hideSetPasswordToast(); + } }; _createResizer() { @@ -597,7 +600,6 @@ class LoggedInView extends React.PureComponent { const ToastContainer = sdk.getComponent('structures.ToastContainer'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); - const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); const ServerLimitBar = sdk.getComponent('globals.ServerLimitBar'); let pageElement; @@ -666,8 +668,6 @@ class LoggedInView extends React.PureComponent { />; } else if (this.props.checkingForUpdate) { topBar = ; - } else if (this.state.userHasGeneratedPassword) { - topBar = ; } let bodyClasses = 'mx_MatrixChat'; diff --git a/src/components/views/globals/PasswordNagBar.js b/src/components/views/globals/PasswordNagBar.js deleted file mode 100644 index 74735ca5ea..0000000000 --- a/src/components/views/globals/PasswordNagBar.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -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 React from 'react'; -import createReactClass from 'create-react-class'; -import * as sdk from '../../../index'; -import Modal from '../../../Modal'; -import { _t } from '../../../languageHandler'; - -export default createReactClass({ - onUpdateClicked: function() { - const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog'); - Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog); - }, - - render: function() { - const toolbarClasses = "mx_MatrixToolbar mx_MatrixToolbar_clickable"; - return ( -
    - -
    - { _t( - "To return to your account in future you need to set a password", - {}, - { 'u': (sub) => { sub } }, - ) } -
    - -
    - ); - }, -}); diff --git a/src/toasts/SetPasswordToast.ts b/src/toasts/SetPasswordToast.ts new file mode 100644 index 0000000000..88cc317978 --- /dev/null +++ b/src/toasts/SetPasswordToast.ts @@ -0,0 +1,47 @@ +/* +Copyright 2020 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 { _t } from "../languageHandler"; +import Modal from "../Modal"; +import SetPasswordDialog from "../components/views/dialogs/SetPasswordDialog"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog); +}; + +const TOAST_KEY = "setpassword"; + +export const showToast = () => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Set password"), + props: { + description: _t("To return to your account in future you need to set a password"), + acceptLabel: _t("Set Password"), + onAccept, + rejectLabel: _t("Later"), + onReject: hideToast, // it'll return on reload + }, + component: GenericToast, + priority: 60, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From ccf9e6512341bb7395b1eba3841b0ce475379da5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:27:19 +0100 Subject: [PATCH 269/399] Migrate Server Limit Bar Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 56 ++++++----- .../views/globals/ServerLimitBar.js | 99 ------------------- src/toasts/ServerLimitToast.tsx | 50 ++++++++++ 3 files changed, 80 insertions(+), 125 deletions(-) delete mode 100644 src/components/views/globals/ServerLimitBar.js create mode 100644 src/toasts/ServerLimitToast.tsx diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 131e6a3867..edb2482aa3 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -47,6 +47,10 @@ import { showToast as showSetPasswordToast, hideToast as hideSetPasswordToast } from "../../toasts/SetPasswordToast"; +import { + showToast as showServerLimitToast, + hideToast as hideServerLimitToast +} from "../../toasts/ServerLimitToast"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -99,7 +103,6 @@ interface IState { }; syncErrorData: any; useCompactLayout: boolean; - serverNoticeEvents: MatrixEvent[]; } /** @@ -144,8 +147,6 @@ class LoggedInView extends React.PureComponent { syncErrorData: undefined, // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), - // any currently active server notice events - serverNoticeEvents: [], }; // stash the MatrixClient in case we log out before we are unmounted @@ -293,6 +294,8 @@ class LoggedInView extends React.PureComponent { if (oldSyncState === 'PREPARED' && syncState === 'SYNCING') { this._updateServerNoticeEvents(); + } else { + this._calculateServerLimitToast(data); } }; @@ -303,11 +306,24 @@ class LoggedInView extends React.PureComponent { } }; + _calculateServerLimitToast(syncErrorData, usageLimitEventContent?) { + const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED"; + if (error) { + usageLimitEventContent = syncErrorData.error.data; + } + + if (usageLimitEventContent) { + showServerLimitToast(usageLimitEventContent.limit_type, usageLimitEventContent.admin_contact, error); + } else { + hideServerLimitToast(); + } + } + _updateServerNoticeEvents = async () => { const roomLists = RoomListStoreTempProxy.getRoomLists(); if (!roomLists[DefaultTagID.ServerNotice]) return []; - const pinnedEvents = []; + const events = []; for (const room of roomLists[DefaultTagID.ServerNotice]) { const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", ""); @@ -317,12 +333,18 @@ class LoggedInView extends React.PureComponent { for (const eventId of pinnedEventIds) { const timeline = await this._matrixClient.getEventTimeline(room.getUnfilteredTimelineSet(), eventId, 0); const event = timeline.getEvents().find(ev => ev.getId() === eventId); - if (event) pinnedEvents.push(event); + if (event) events.push(event); } } - this.setState({ - serverNoticeEvents: pinnedEvents, + + const usageLimitEvent = events.find((e) => { + return ( + e && e.getType() === 'm.room.message' && + e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' + ); }); + + this._calculateServerLimitToast(this.state.syncErrorData, usageLimitEvent && usageLimitEvent.getContent()); }; _onPaste = (ev) => { @@ -600,7 +622,6 @@ class LoggedInView extends React.PureComponent { const ToastContainer = sdk.getComponent('structures.ToastContainer'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); - const ServerLimitBar = sdk.getComponent('globals.ServerLimitBar'); let pageElement; @@ -644,25 +665,8 @@ class LoggedInView extends React.PureComponent { break; } - const usageLimitEvent = this.state.serverNoticeEvents.find((e) => { - return ( - e && e.getType() === 'm.room.message' && - e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' - ); - }); - let topBar; - if (this.state.syncErrorData && this.state.syncErrorData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { - topBar = ; - } else if (usageLimitEvent) { - topBar = ; - } else if (this.props.hasNewVersion) { + if (this.props.hasNewVersion) { topBar = ; diff --git a/src/components/views/globals/ServerLimitBar.js b/src/components/views/globals/ServerLimitBar.js deleted file mode 100644 index 7d414a2826..0000000000 --- a/src/components/views/globals/ServerLimitBar.js +++ /dev/null @@ -1,99 +0,0 @@ -/* -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 React from 'react'; -import PropTypes from 'prop-types'; -import createReactClass from 'create-react-class'; -import classNames from 'classnames'; -import { _td } from '../../../languageHandler'; -import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; - -export default createReactClass({ - propTypes: { - // 'hard' if the logged in user has been locked out, 'soft' if they haven't - kind: PropTypes.string, - adminContact: PropTypes.string, - // The type of limit that has been hit. - limitType: PropTypes.string.isRequired, - }, - - getDefaultProps: function() { - return { - kind: 'hard', - }; - }, - - render: function() { - const toolbarClasses = { - 'mx_MatrixToolbar': true, - }; - - let adminContact; - let limitError; - if (this.props.kind === 'hard') { - toolbarClasses['mx_MatrixToolbar_error'] = true; - - adminContact = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - '': _td("Please contact your service administrator to continue using the service."), - }, - ); - limitError = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), - '': _td("This homeserver has exceeded one of its resource limits."), - }, - ); - } else { - toolbarClasses['mx_MatrixToolbar_info'] = true; - adminContact = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - '': _td("Please contact your service administrator to get this limit increased."), - }, - ); - limitError = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - 'monthly_active_user': _td( - "This homeserver has hit its Monthly Active User limit so " + - "some users will not be able to log in.", - ), - '': _td( - "This homeserver has exceeded one of its resource limits so " + - "some users will not be able to log in.", - ), - }, - {'b': sub => {sub}}, - ); - } - return ( -
    -
    - {limitError} - {' '} - {adminContact} -
    -
    - ); - }, -}); diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx new file mode 100644 index 0000000000..f2de9e3499 --- /dev/null +++ b/src/toasts/ServerLimitToast.tsx @@ -0,0 +1,50 @@ +/* +Copyright 2020 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 { _t, _td } from "../languageHandler"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; +import {messageForResourceLimitError} from "../utils/ErrorUtils"; + +const TOAST_KEY = "serverlimit"; + +export const showToast = (limitType: string, adminContact?: string, syncError?: boolean) => { + const errorText = messageForResourceLimitError(limitType, adminContact, { + 'monthly_active_user': _td("Your homeserver has exceeded its user limit."), + '': _td("Your homeserver has exceeded one of its resource limits."), + }); + const contactText = messageForResourceLimitError(limitType, adminContact, { + '': _td("Contact your server admin."), + }); + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: {errorText} {contactText}, + acceptLabel: _t("Ok"), + onAccept: hideToast, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 29cfb47a83f5fc016f941a8387efb8f5a3e7463f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:29:09 +0100 Subject: [PATCH 270/399] fix copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/AnalyticsToast.tsx | 2 +- src/toasts/ServerLimitToast.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index 5b53a903fe..b186a65d9d 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -44,7 +44,7 @@ const TOAST_KEY = "analytics"; export const showToast = (policyUrl?: string) => { ToastStore.sharedInstance().addOrReplaceToast({ key: TOAST_KEY, - title: _t("Notifications"), + title: _t("Help us improve Riot"), props: { description: _t( "Send anonymous usage data which helps us improve Riot. " + diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx index f2de9e3499..d35140be3d 100644 --- a/src/toasts/ServerLimitToast.tsx +++ b/src/toasts/ServerLimitToast.tsx @@ -34,14 +34,14 @@ export const showToast = (limitType: string, adminContact?: string, syncError?: ToastStore.sharedInstance().addOrReplaceToast({ key: TOAST_KEY, - title: _t("Notifications"), + title: _t("Warning"), props: { description: {errorText} {contactText}, acceptLabel: _t("Ok"), onAccept: hideToast, }, component: GenericToast, - priority: 20, + priority: 70, }); }; From 9f060d113259d3cf22d5e9ee4f6a98eb91c0d5d7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:34:32 +0100 Subject: [PATCH 271/399] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a79ee97109..72c19587c8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -391,7 +391,7 @@ "Common names and surnames are easy to guess": "Common names and surnames are easy to guess", "Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess", "Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess", - "Notifications": "Notifications", + "Help us improve Riot": "Help us improve Riot", "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Send anonymous usage data which helps us improve Riot. This will use a cookie.", "I want to help": "I want to help", "No": "No", @@ -399,9 +399,18 @@ "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", "Review": "Review", "Later": "Later", + "Notifications": "Notifications", "You are not receiving desktop notifications": "You are not receiving desktop notifications", "Enable them now": "Enable them now", "Close": "Close", + "Your homeserver has exceeded its user limit.": "Your homeserver has exceeded its user limit.", + "Your homeserver has exceeded one of its resource limits.": "Your homeserver has exceeded one of its resource limits.", + "Contact your server admin.": "Contact your server admin.", + "Warning": "Warning", + "Ok": "Ok", + "Set password": "Set password", + "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", + "Set Password": "Set Password", "Set up encryption": "Set up encryption", "Encryption upgrade available": "Encryption upgrade available", "Verify this session": "Verify this session", @@ -781,7 +790,6 @@ "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", "Deactivate Account": "Deactivate Account", - "Warning": "Warning", "General": "General", "Discovery": "Discovery", "Deactivate account": "Deactivate account", @@ -1383,18 +1391,10 @@ "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "You're not currently a member of any communities.": "You're not currently a member of any communities.", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.", - "Yes, I want to help!": "Yes, I want to help!", "What's New": "What's New", "Update": "Update", "What's new?": "What's new?", "A new version of Riot is available.": "A new version of Riot is available.", - "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", - "Set Password": "Set Password", - "Please contact your service administrator to get this limit increased.": "Please contact your service administrator to get this limit increased.", - "This homeserver has hit its Monthly Active User limit so some users will not be able to log in.": "This homeserver has hit its Monthly Active User limit so some users will not be able to log in.", - "This homeserver has exceeded one of its resource limits so some users will not be able to log in.": "This homeserver has exceeded one of its resource limits so some users will not be able to log in.", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "Checking for an update...": "Checking for an update...", "No update available.": "No update available.", From 891ba1bbe3c34bf5651846d425c07d969fec7cb7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:08:45 +0100 Subject: [PATCH 272/399] Replace New Version Bar with a Toast discards the `new_version` dispatch Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/globals/_MatrixToolbar.scss | 4 - src/components/structures/LoggedInView.tsx | 11 +- src/components/structures/MatrixChat.tsx | 21 ---- src/components/views/globals/NewVersionBar.js | 108 ------------------ src/i18n/strings/en_EN.json | 10 +- src/toasts/UpdateToast.tsx | 90 +++++++++++++++ 6 files changed, 99 insertions(+), 145 deletions(-) delete mode 100644 src/components/views/globals/NewVersionBar.js create mode 100644 src/toasts/UpdateToast.tsx diff --git a/res/css/views/globals/_MatrixToolbar.scss b/res/css/views/globals/_MatrixToolbar.scss index 5fdf572f99..07b92a7235 100644 --- a/res/css/views/globals/_MatrixToolbar.scss +++ b/res/css/views/globals/_MatrixToolbar.scss @@ -67,7 +67,3 @@ limitations under the License. .mx_MatrixToolbar_action { margin-right: 16px; } - -.mx_MatrixToolbar_changelog { - white-space: pre; -} diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index edb2482aa3..2a17233ec6 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -74,7 +74,6 @@ interface IProps { initialEventPixelOffset: number; leftDisabled: boolean; rightDisabled: boolean; - hasNewVersion: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -96,6 +95,7 @@ interface IProps { newVersion?: string; newVersionReleaseNotes?: string; } + interface IState { mouseDown?: { x: number; @@ -183,7 +183,7 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.hasNewVersion !== this.props.hasNewVersion) + (prevProps.checkingForUpdate !== this.props.checkingForUpdate) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -620,7 +620,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); let pageElement; @@ -666,11 +665,7 @@ class LoggedInView extends React.PureComponent { } let topBar; - if (this.props.hasNewVersion) { - topBar = ; - } else if (this.props.checkingForUpdate) { + if (this.props.checkingForUpdate) { topBar = ; } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index c69911fe04..f4d31708fe 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -173,10 +173,6 @@ interface IState { leftDisabled: boolean; middleDisabled: boolean; // the right panel's disabled state is tracked in its store. - version?: string; - newVersion?: string; - hasNewVersion: boolean; - newVersionReleaseNotes?: string; checkingForUpdate?: string; // updateCheckStatusEnum // Parameters used in the registration dance with the IS register_client_secret?: string; @@ -230,7 +226,6 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - hasNewVersion: false, newVersionReleaseNotes: null, checkingForUpdate: null, @@ -726,12 +721,6 @@ export default class MatrixChat extends React.PureComponent { case 'client_started': this.onClientStarted(); break; - case 'new_version': - this.onVersion( - payload.currentVersion, payload.newVersion, - payload.releaseNotes, - ); - break; case 'check_updates': this.setState({ checkingForUpdate: payload.value }); break; @@ -1820,16 +1809,6 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("settings"); }; - onVersion(current: string, latest: string, releaseNotes?: string) { - this.setState({ - version: current, - newVersion: latest, - hasNewVersion: current !== latest, - newVersionReleaseNotes: releaseNotes, - checkingForUpdate: null, - }); - } - onSendEvent(roomId: string, event: MatrixEvent) { const cli = MatrixClientPeg.get(); if (!cli) { diff --git a/src/components/views/globals/NewVersionBar.js b/src/components/views/globals/NewVersionBar.js deleted file mode 100644 index dedccdc6b6..0000000000 --- a/src/components/views/globals/NewVersionBar.js +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> - -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 createReactClass from 'create-react-class'; -import * as sdk from '../../../index'; -import Modal from '../../../Modal'; -import PlatformPeg from '../../../PlatformPeg'; -import { _t } from '../../../languageHandler'; - -/** - * Check a version string is compatible with the Changelog - * dialog ([vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]) - */ -function checkVersion(ver) { - const parts = ver.split('-'); - return parts.length == 5 && parts[1] == 'react' && parts[3] == 'js'; -} - -export default createReactClass({ - propTypes: { - version: PropTypes.string.isRequired, - newVersion: PropTypes.string.isRequired, - releaseNotes: PropTypes.string, - }, - - displayReleaseNotes: function(releaseNotes) { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - Modal.createTrackedDialog('Display release notes', '', QuestionDialog, { - title: _t("What's New"), - description:
    {releaseNotes}
    , - button: _t("Update"), - onFinished: (update) => { - if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); - } - }, - }); - }, - - displayChangelog: function() { - const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog'); - Modal.createTrackedDialog('Display Changelog', '', ChangelogDialog, { - version: this.props.version, - newVersion: this.props.newVersion, - onFinished: (update) => { - if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); - } - }, - }); - }, - - onUpdateClicked: function() { - PlatformPeg.get().installUpdate(); - }, - - render: function() { - let action_button; - // If we have release notes to display, we display them. Otherwise, - // we display the Changelog Dialog which takes two versions and - // automatically tells you what's changed (provided the versions - // are in the right format) - if (this.props.releaseNotes) { - action_button = ( - - ); - } else if (checkVersion(this.props.version) && checkVersion(this.props.newVersion)) { - action_button = ( - - ); - } else if (PlatformPeg.get()) { - action_button = ( - - ); - } - return ( -
    - -
    - {_t("A new version of Riot is available.")} -
    - {action_button} -
    - ); - }, -}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 72c19587c8..0170aee17a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -421,6 +421,12 @@ "Other users may not trust it": "Other users may not trust it", "New login. Was this you?": "New login. Was this you?", "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", + "What's New": "What's New", + "Update": "Update", + "What's new?": "What's new?", + "Restart": "Restart", + "Upgrade your Riot": "Upgrade your Riot", + "A new version of Riot is available!": "A new version of Riot is available!", "There was an error joining the room": "There was an error joining the room", "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", @@ -1391,10 +1397,6 @@ "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "You're not currently a member of any communities.": "You're not currently a member of any communities.", - "What's New": "What's New", - "Update": "Update", - "What's new?": "What's new?", - "A new version of Riot is available.": "A new version of Riot is available.", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "Checking for an update...": "Checking for an update...", "No update available.": "No update available.", diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx new file mode 100644 index 0000000000..3d4b55a4ff --- /dev/null +++ b/src/toasts/UpdateToast.tsx @@ -0,0 +1,90 @@ +/* +Copyright 2020 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 { _t } from "../languageHandler"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; +import QuestionDialog from "../components/views/dialogs/QuestionDialog"; +import ChangelogDialog from "../components/views/dialogs/ChangelogDialog"; +import PlatformPeg from "../PlatformPeg"; +import Modal from "../Modal"; + +const TOAST_KEY = "update"; + +/* + * Check a version string is compatible with the Changelog + * dialog ([riot-version]-react-[react-sdk-version]-js-[js-sdk-version]) + */ +function checkVersion(ver) { + const parts = ver.split('-'); + return parts.length === 5 && parts[1] === 'react' && parts[3] === 'js'; +} + +function installUpdate() { + PlatformPeg.get().installUpdate(); +} + +export const showToast = (version: string, newVersion: string, releaseNotes?: string) => { + let onAccept; + let acceptLabel = _t("What's new?"); + if (releaseNotes) { + onAccept = () => { + Modal.createTrackedDialog('Display release notes', '', QuestionDialog, { + title: _t("What's New"), + description:
    {releaseNotes}
    , + button: _t("Update"), + onFinished: (update) => { + if (update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } + }, + }); + }; + } else if (checkVersion(version) && checkVersion(newVersion)) { + onAccept = () => { + Modal.createTrackedDialog('Display Changelog', '', ChangelogDialog, { + version, + newVersion, + onFinished: (update) => { + if (update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } + }, + }); + }; + } else { + onAccept = installUpdate; + acceptLabel = _t("Restart"); + } + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Upgrade your Riot"), + props: { + description: _t("A new version of Riot is available!"), + acceptLabel, + onAccept, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 41934268087fa0b9be828f1714eb4a5e3ab6f28a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:14:33 +0100 Subject: [PATCH 273/399] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 3 --- src/components/structures/MatrixChat.tsx | 1 - 2 files changed, 4 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 2a17233ec6..df21768da5 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -91,9 +91,6 @@ interface IProps { currentUserId?: string; currentGroupId?: string; currentGroupIsNew?: boolean; - version?: string; - newVersion?: string; - newVersionReleaseNotes?: string; } interface IState { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f4d31708fe..e6db42af1d 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -226,7 +226,6 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - newVersionReleaseNotes: null, checkingForUpdate: null, hideToSRUsers: false, From 3d2b56aecd38ea399f2666b688c4007438384411 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:23:23 +0100 Subject: [PATCH 274/399] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0170aee17a..97ada284b0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -421,9 +421,9 @@ "Other users may not trust it": "Other users may not trust it", "New login. Was this you?": "New login. Was this you?", "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", + "What's new?": "What's new?", "What's New": "What's New", "Update": "Update", - "What's new?": "What's new?", "Restart": "Restart", "Upgrade your Riot": "Upgrade your Riot", "A new version of Riot is available!": "A new version of Riot is available!", From a19e27d11c27c97d4a731720d8b636380865b72b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 22 May 2020 18:05:09 -0600 Subject: [PATCH 275/399] Calculate badges in the new room list more reliably Fixes https://github.com/vector-im/riot-web/issues/13757 This uses a different way of calculating the badges by using about 6 less variables, and consolidating the remaining ones down. --- src/components/views/rooms/RoomTile2.tsx | 109 +++++++++++++++-------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 42b65cba87..e23b17a726 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -39,6 +39,14 @@ import * as FormattingUtils from "../../../utils/FormattingUtils"; * warning disappears. * *******************************************************************/ +enum NotificationColor { + // Inverted (None -> Red) because we do integer comparisons on this + None, // nothing special + Bold, // no badge, show as unread + Grey, // unread notified messages + Red, // unread pings +} + interface IProps { room: Room; @@ -47,16 +55,14 @@ interface IProps { // TODO: Incoming call boxes? } -interface IBadgeState { - showBadge: boolean; // if numUnread > 0 && !showBadge -> bold room - numUnread: number; // used only if showBadge or showBadgeHighlight is true - hasUnread: number; // used to make the room bold - showBadgeHighlight: boolean; // make the badge red - isInvite: boolean; // show a `!` instead of a number +interface INotificationState { + symbol: string; + color: NotificationColor; } -interface IState extends IBadgeState { +interface IState { hover: boolean; + notificationState: INotificationState; } export default class RoomTile2 extends React.Component { @@ -78,8 +84,7 @@ export default class RoomTile2 extends React.Component { this.state = { hover: false, - - ...this.getBadgeState(), + notificationState: this.getNotificationState(), }; } @@ -87,28 +92,57 @@ export default class RoomTile2 extends React.Component { // TODO: Listen for changes to the badge count and update as needed } - private updateBadgeCount() { - this.setState({...this.getBadgeState()}); + // XXX: This is a bit of an awful-looking hack. We should probably be using state for + // this, but instead we're kinda forced to either duplicate the code or thread a variable + // through the code paths. This feels like the least evil + private get roomIsInvite(): boolean { + return getEffectiveMembership(this.props.room.getMyMembership()) === EffectiveMembership.Invite; } - private getBadgeState(): IBadgeState { - // TODO: Make this code path faster - const highlightCount = RoomNotifs.getUnreadNotificationCount(this.props.room, 'highlight'); - const numUnread = RoomNotifs.getUnreadNotificationCount(this.props.room); - const showBadge = Unread.doesRoomHaveUnreadMessages(this.props.room); - const myMembership = getEffectiveMembership(this.props.room.getMyMembership()); - const isInvite = myMembership === EffectiveMembership.Invite; - const notifState = RoomNotifs.getRoomNotifsState(this.props.room.roomId); - const shouldShowNotifBadge = RoomNotifs.shouldShowNotifBadge(notifState); - const shouldShowHighlightBadge = RoomNotifs.shouldShowMentionBadge(notifState); + private updateNotificationState() { + this.setState({notificationState: this.getNotificationState()}); + } - return { - showBadge: (showBadge && shouldShowNotifBadge) || isInvite, - numUnread, - hasUnread: showBadge, - showBadgeHighlight: (highlightCount > 0 && shouldShowHighlightBadge) || isInvite, - isInvite, + private getNotificationState(): INotificationState { + const state: INotificationState = { + color: NotificationColor.None, + symbol: null, }; + + if (this.roomIsInvite) { + state.color = NotificationColor.Red; + state.symbol = "!"; + } else { + const redNotifs = RoomNotifs.getUnreadNotificationCount(this.props.room, 'highlight'); + const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.props.room, 'total'); + + // For a 'true count' we pick the grey notifications first because they include the + // red notifications. If we don't have a grey count for some reason we use the red + // count. If that count is broken for some reason, assume zero. This avoids us showing + // a badge for 'NaN' (which formats as 'NaNB' for NaN Billion). + const trueCount = greyNotifs ? greyNotifs : (redNotifs ? redNotifs : 0); + + // Note: we only set the symbol if we have an actual count. We don't want to show + // zero on badges. + + if (redNotifs > 0) { + state.color = NotificationColor.Red; + state.symbol = FormattingUtils.formatCount(trueCount); + } else if (greyNotifs > 0) { + state.color = NotificationColor.Grey; + state.symbol = FormattingUtils.formatCount(trueCount); + } else { + // We don't have any notified messages, but we might have unread messages. Let's + // find out. + const hasUnread = Unread.doesRoomHaveUnreadMessages(this.props.room); + if (hasUnread) { + state.color = NotificationColor.Bold; + // no symbol for this state + } + } + } + + return state; } private onTileMouseEnter = () => { @@ -135,15 +169,17 @@ export default class RoomTile2 extends React.Component { // TODO: a11y proper // TODO: Render more than bare minimum + const hasBadge = this.state.notificationState.color > NotificationColor.Bold; + const isUnread = this.state.notificationState.color > NotificationColor.None; const classes = classNames({ 'mx_RoomTile': true, // 'mx_RoomTile_selected': this.state.selected, - 'mx_RoomTile_unread': this.state.numUnread > 0 || this.state.hasUnread, - 'mx_RoomTile_unreadNotify': this.state.showBadge, - 'mx_RoomTile_highlight': this.state.showBadgeHighlight, - 'mx_RoomTile_invited': this.state.isInvite, + 'mx_RoomTile_unread': isUnread, + 'mx_RoomTile_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey, + 'mx_RoomTile_highlight': this.state.notificationState.color >= NotificationColor.Red, + 'mx_RoomTile_invited': this.roomIsInvite, // 'mx_RoomTile_menuDisplayed': isMenuDisplayed, - 'mx_RoomTile_noBadges': !this.state.showBadge, + 'mx_RoomTile_noBadges': !hasBadge, // 'mx_RoomTile_transparent': this.props.transparent, // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed, }); @@ -154,13 +190,12 @@ export default class RoomTile2 extends React.Component { let badge; - if (this.state.showBadge) { + if (hasBadge) { const badgeClasses = classNames({ 'mx_RoomTile_badge': true, 'mx_RoomTile_badgeButton': false, // this.state.badgeHover || isMenuDisplayed }); - const formattedCount = this.state.isInvite ? `!` : FormattingUtils.formatCount(this.state.numUnread); - badge =
    {formattedCount}
    ; + badge =
    {this.state.notificationState.symbol}
    ; } // TODO: the original RoomTile uses state for the room name. Do we need to? @@ -170,8 +205,8 @@ export default class RoomTile2 extends React.Component { const nameClasses = classNames({ 'mx_RoomTile_name': true, - 'mx_RoomTile_invite': this.state.isInvite, - 'mx_RoomTile_badgeShown': this.state.showBadge, + 'mx_RoomTile_invite': this.roomIsInvite, + 'mx_RoomTile_badgeShown': hasBadge, }); // TODO: Support collapsed state properly From 3a3ddbf7c418d0af9dde13a2a7b1617df65ff945 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 22 May 2020 18:10:30 -0600 Subject: [PATCH 276/399] Add missing documentation --- src/components/views/rooms/RoomTile2.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index e23b17a726..8b1beee713 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -94,11 +94,12 @@ export default class RoomTile2 extends React.Component { // XXX: This is a bit of an awful-looking hack. We should probably be using state for // this, but instead we're kinda forced to either duplicate the code or thread a variable - // through the code paths. This feels like the least evil + // through the code paths. This feels like the least evil option. private get roomIsInvite(): boolean { return getEffectiveMembership(this.props.room.getMyMembership()) === EffectiveMembership.Invite; } + // TODO: Make use of this function when the notification state needs updating. private updateNotificationState() { this.setState({notificationState: this.getNotificationState()}); } From fe03d02cf62b4656a47635dce28af58ef01f091d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 23 May 2020 08:43:15 +0100 Subject: [PATCH 277/399] Fix reacting to redactions Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- 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 ac7ac8c9ec..6558a11ed4 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -31,7 +31,7 @@ export function isContentActionable(mxEvent) { // status is SENT before remote-echo, null after const isSent = !eventStatus || eventStatus === EventStatus.SENT; - if (isSent) { + if (isSent && !mxEvent.isRedacted()) { if (mxEvent.getType() === 'm.room.message') { const content = mxEvent.getContent(); if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) { From 066dd4b61140b14db91d52ae63f8b59caa32c861 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 23 May 2020 08:48:28 +0100 Subject: [PATCH 278/399] Update Modular hosting link Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/ModularServerConfig.js | 3 ++- src/components/views/auth/ServerTypeSelector.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index 1216202a23..591c30ee7c 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -23,7 +23,8 @@ import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; import * as ServerType from '../../views/auth/ServerTypeSelector'; import ServerConfig from "./ServerConfig"; -const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; +const MODULAR_URL = 'https://modular.im/services/matrix-hosting-riot' + + '?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; // TODO: TravisR - Can this extend ServerConfig for most things? diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js index fe29b7f76c..a8a1dda968 100644 --- a/src/components/views/auth/ServerTypeSelector.js +++ b/src/components/views/auth/ServerTypeSelector.js @@ -22,7 +22,8 @@ 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'; +const MODULAR_URL = 'https://modular.im/services/matrix-hosting-riot' + + '?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; export const FREE = 'Free'; export const PREMIUM = 'Premium'; From d29ba5b597db302a457bea0574603d93474c842d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 23 May 2020 09:02:35 +0100 Subject: [PATCH 279/399] fix priority sorting to highest first Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 4f6d2963c5..23317a0ad3 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -55,7 +55,7 @@ export default class ToastStore extends EventEmitter { const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; - while (newIndex > 0 && this.toasts[newIndex - 1].priority > newToast.priority) --newIndex; + while (newIndex > 0 && this.toasts[newIndex - 1].priority < newToast.priority) --newIndex; this.toasts.splice(newIndex, 0, newToast); } else { this.toasts[oldIndex] = newToast; From 6a48b548897f2bca8925575e9e911209d06b1c8b Mon Sep 17 00:00:00 2001 From: Osoitz Date: Fri, 22 May 2020 14:08:58 +0000 Subject: [PATCH 280/399] Translated using Weblate (Basque) Currently translated at 100.0% (2322 of 2322 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 | 44 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 6548ea33d1..6624fa99bb 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -783,7 +783,7 @@ "Explore Account Data": "Miatu kontuaren datuak", "All messages (noisy)": "Mezu guztiak (ozen)", "Saturday": "Larunbata", - "I understand the risks and wish to continue": "Arriskua ulertzen dut eta jarraitu nahi dut", + "I understand the risks and wish to continue": "Arriskuak ulertzen ditut eta jarraitu nahi dut", "Direct Chat": "Txat zuzena", "The server may be unavailable or overloaded": "Zerbitzaria eskuraezin edo gainezka egon daiteke", "Reject": "Baztertu", @@ -2385,5 +2385,45 @@ "Please enter your recovery passphrase a second time to confirm.": "Sartu zure berreskuratze pasa-esaldia berriro baieztatzeko.", "Repeat your recovery passphrase...": "Errepikatu zure berreskuratze pasa-esaldia...", "Secure your backup with a recovery passphrase": "Babestu zure babeskopia berreskuratze pasa-esaldi batekin", - "Currently indexing: %(currentRoom)s": "Orain indexatzen: %(currentRoom)s" + "Currently indexing: %(currentRoom)s": "Orain indexatzen: %(currentRoom)s", + "Review where you’re logged in": "Berrikusi non hasi duzun saioa", + "New login. Was this you?": "Saio berria. Zu izan zara?", + "Opens chat with the given user": "Erabiltzailearekin txata irekitzen du", + "Sends a message to the given user": "Erabiltzaileari mezua bidaltzen dio", + "You signed in to a new session without verifying it:": "Saio berria hasi duzu hau egiaztatu gabe:", + "Verify your other session using one of the options below.": "Egiaztatu zure beste saioa beheko aukeretako batekin.", + "Font scaling": "Letren eskalatzea", + "Use the improved room list (in development - refresh to apply changes)": "Erabili gelen zerrenda hobetua (garapenean, freskatu aldaketak aplikatzedko)", + "Use IRC layout": "Erabili IRC diseinua", + "Font size": "Letra-tamaina", + "Custom font size": "Letra-tamaina pertsonalizatua", + "IRC display name width": "IRC-ko pantaila izenaren zabalera", + "Waiting for your other session to verify…": "Zure beste saioak egiaztatu bitartean zain…", + "Verify all your sessions to ensure your account & messages are safe": "Egiaztatu zure saio guztiak kontua eta mezuak seguru daudela bermatzeko", + "Verify the new login accessing your account: %(name)s": "Egiaztatu zure kontuan hasitako saio berria: %(name)s", + "Size must be a number": "Tamaina zenbaki bat izan behar da", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Letra tamaina pertsonalizatua %(min)s pt eta %(max)s pt bitartean egon behar du", + "Use between %(min)s pt and %(max)s pt": "Erabili %(min)s pt eta %(max)s pt bitarteko balioa", + "Appearance": "Itxura", + "Where you’re logged in": "Non hasi duzun saioa", + "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Kudeatu azpiko saioen izenak eta hauek amaitu edo egiaztatu zure erabiltzaile-profilean.", + "Create room": "Sortu gela", + "You've successfully verified your device!": "Ongi egiaztatu duzu zure gailua!", + "Message deleted": "Mezu ezabatuta", + "Message deleted by %(name)s": "Mezua ezabatu du %(name)s erabiltzaileak", + "QR Code": "QR kodea", + "To continue, use Single Sign On to prove your identity.": "Jarraitzeko, erabili Single Sign On zure identitatea frogatzeko.", + "Confirm to continue": "Berretsi jarraitzeko", + "Click the button below to confirm your identity.": "Sakatu azpiko botoia zure identitatea frogatzeko.", + "Invite someone using their name, username (like ), email address or share this room.": "Gonbidatu norbait bere izena, erabiltzaile izena (esaterako ), e-mail helbidea erabiliz, edo partekatu gela hau.", + "Restoring keys from backup": "Gakoak babes-kopiatik berrezartzen", + "Fetching keys from server...": "Gakoak zerbitzaritik eskuratzen...", + "%(completed)s of %(total)s keys restored": "%(completed)s/%(total)s gako berreskuratuta", + "Keys restored": "Gakoak berreskuratuta", + "Successfully restored %(sessionCount)s keys": "%(sessionCount)s gako ongi berreskuratuta", + "Confirm encryption setup": "Berretsi zifratze ezarpena", + "Click the button below to confirm setting up encryption.": "Sakatu azpiko botoia zifratze-ezarpena berresteko.", + "Dismiss read marker and jump to bottom": "Baztertu irakurtze-marka eta jauzi beheraino", + "Jump to oldest unread message": "Jauzi irakurri gabeko mezu zaharrenera", + "Upload a file": "Igo fitxategia" } From 020a1841c711f310c260b595f22485fe41f88843 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Sat, 23 May 2020 05:16:28 +0000 Subject: [PATCH 281/399] Translated using Weblate (Bulgarian) Currently translated at 94.6% (2196 of 2322 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 347823f95c..186fb7c6db 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -2211,5 +2211,59 @@ "Invite someone using their name, username (like ), email address or share this room.": "Поканете някой посредством име, потребителско име (като ), имейл адрес или като споделите тази стая.", "You added a new session '%(displayName)s', which is requesting encryption keys.": "Добавихте нова сесия '%(displayName)s', която изисква ключове за шифроване.", "Your unverified session '%(displayName)s' is requesting encryption keys.": "Непотвърдената ви сесия '%(displayName)s' изисква ключове за шифроване.", - "Loading session info...": "Зареждане на информация за сесията..." + "Loading session info...": "Зареждане на информация за сесията...", + "Opens chat with the given user": "Отваря чат с дадения потребител", + "Sends a message to the given user": "Изпраща съобщение до дадения потребител", + "Font scaling": "Мащабиране на шрифта", + "Use the improved room list (in development - refresh to apply changes)": "Използвай подобрения списък със стаи (в процес на разработка - презаредете за да приложите промените)", + "Use IRC layout": "Използвай IRC изглед", + "Font size": "Размер на шрифта", + "Custom font size": "Собствен размер на шрифта", + "IRC display name width": "Ширина на IRC името", + "Waiting for your other session to verify…": "Изчакване другата сесията да потвърди…", + "Size must be a number": "Размера трябва да е число", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Собствения размер на шрифта може да бъде единствено между %(min)s pt и %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Изберете между %(min)s pt и %(max)s pt", + "Appearance": "Изглед", + "Create room": "Създай стая", + "You've successfully verified your device!": "Успешно потвърдихте устройството си!", + "Message deleted": "Съобщението беше изтрито", + "Message deleted by %(name)s": "Съобщението беше изтрито от %(name)s", + "QR Code": "QR код", + "To continue, use Single Sign On to prove your identity.": "За да продължите, използвайте Single Sign On за да потвърдите самоличността си.", + "Confirm to continue": "Потвърдете за да продължите", + "Click the button below to confirm your identity.": "Кликнете бутона по-долу за да потвърдите самоличността си.", + "a new master key signature": "нов подпис на основния ключ", + "a new cross-signing key signature": "нов подпис на ключа за кръстосано-подписване", + "a device cross-signing signature": "подпис за кръстосано-подписване на устройства", + "a key signature": "подпис на ключ", + "Riot encountered an error during upload of:": "Riot срещна проблем при качването на:", + "Upload completed": "Качването завърши", + "Cancelled signature upload": "Отказано качване на подпис", + "Unable to upload": "Неуспешно качване", + "Signature upload success": "Успешно качване на подпис", + "Signature upload failed": "Неуспешно качване на подпис", + "Confirm by comparing the following with the User Settings in your other session:": "Потвърдете чрез сравняване на следното с Потребителски Настройки в другата ви сесия:", + "Confirm this user's session by comparing the following with their User Settings:": "Потвърдете сесията на този потребител чрез сравняване на следното с техните Потребителски Настройки:", + "If they don't match, the security of your communication may be compromised.": "Ако няма съвпадение, сигурността на комуникацията ви може би е компрометирана.", + "Your account is not secure": "Профилът ви не е защитен", + "Your password": "Паролата ви", + "This session, or the other session": "Тази сесия или другата сесия", + "The internet connection either session is using": "Интернет връзката, която сесиите използват", + "We recommend you change your password and recovery key in Settings immediately": "Препоръчваме веднага да промените паролата и ключа за възстановяване от Настройки", + "New session": "Нова сесия", + "Use this session to verify your new one, granting it access to encrypted messages:": "Използвайте тази сесия за да потвърдите новата, давайки й достъп до шифрованите съобщения:", + "If you didn’t sign in to this session, your account may be compromised.": "Ако не сте се вписвали в тази сесия, профила ви може би е бил компрометиран.", + "This wasn't me": "Не бях аз", + "This will allow you to return to your account after signing out, and sign in on other sessions.": "Това ще ви позволи да се върнете в профила си след излизането от него, както и да влизате от други сесии.", + "You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "В момента блокирате непотвърдени сесии; за да изпратите съобщения до тях ще трябва да ги потвърдите.", + "We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Препоръчваме да минете през процеса на потвърждение за всяка една сесия и да проверите дали принадлежи на собственика си, но ако желаете, може да изпратите съобщението и без потвърждение.", + "Room contains unknown sessions": "Стаята съдържа непознати сесии", + "\"%(RoomName)s\" contains sessions that you haven't seen before.": "\"%(RoomName)s\" съдържа сесии, които не сте виждали досега.", + "Unknown sessions": "Непознати сесии", + "Verify other session": "Потвърди другата сесия", + "Enter recovery passphrase": "Въведете парола за възстановяване", + "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Неуспешен достъп до секретното складиране. Потвърдете, че сте въвели правилната парола за възстановяване.", + "Warning: You should only do this on a trusted computer.": "Внимание: трябва да правите това само от доверен компютър.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Въведете паролата си за възстановяване за да достъпите защитената история на съобщенията и самоличността си за кръстосано-подписване за потвърждаване на другите сесии." } From e751b789aa533912db22ba4701c108d5d1880853 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Sat, 23 May 2020 00:42:05 +0000 Subject: [PATCH 282/399] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2322 of 2322 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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 7c55826005..9495c01db7 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2444,5 +2444,6 @@ "Size must be a number": "大小必須為數字", "Custom font size can only be between %(min)s pt and %(max)s pt": "自訂字型大小僅能為 %(min)s 點至 %(max)s 點間", "Use between %(min)s pt and %(max)s pt": "使用 %(min)s 點至 %(max)s 點間", - "Appearance": "外觀" + "Appearance": "外觀", + "Use the improved room list (in development - refresh to apply changes)": "使用改進的聊天室清單(開發中 - 重新整理以套用變更)" } From 77910d2d54e15d318c26c68297c81e9ff01edd43 Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Fri, 22 May 2020 17:04:51 +0000 Subject: [PATCH 283/399] Translated using Weblate (Finnish) Currently translated at 94.5% (2195 of 2322 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 | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index bf2566990c..05bf538ff9 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -421,7 +421,7 @@ "Invite new community members": "Kutsu uusia jäseniä yhteisöön", "Invite to Community": "Kutsu yhteisöön", "Which rooms would you like to add to this community?": "Mitkä huoneet haluaisit lisätä tähän yhteisöön?", - "Show these rooms to non-members on the community page and room list?": "Näytetäänkö nämä huoneet ei-jäsenille yhteisön sivulla ja huonelistassa?", + "Show these rooms to non-members on the community page and room list?": "Näytetäänkö nämä huoneet ei-jäsenille yhteisön sivulla ja huoneluettelossa?", "Add rooms to the community": "Lisää huoneita tähän yhteisöön", "Room name or alias": "Huoneen nimi tai alias", "Add to community": "Lisää yhteisöön", @@ -1343,7 +1343,7 @@ "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 on salattu osapuolten välisellä salauksella vanhalla versiolla, 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, vie salausavaimesi ja tuo ne uudelleen.", "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot epäonnistui protokollalistan hakemisessa kotipalvelimelta. Kotipalvelin saattaa olla liian vanha tukeakseen kolmannen osapuolen verkkoja.", - "Riot failed to get the public room list.": "Riot epäonnistui julkisen huonelistan haussa.", + "Riot failed to get the public room list.": "Riot ei onnistunut hakemaan julkista huoneluetteloa.", "The homeserver may be unavailable or overloaded.": "Kotipalvelin saattaa olla saavuttamattomissa tai ylikuormitettuna.", "You have %(count)s unread notifications in a prior version of this room.|other": "Sinulla on %(count)s lukematonta ilmoitusta huoneen edellisessä versiossa.", "You have %(count)s unread notifications in a prior version of this room.|one": "Sinulla on %(count)s lukematon ilmoitus huoneen edellisessä versiossa.", @@ -1374,7 +1374,7 @@ "Set up Secure Messages": "Ota käyttöön salatut viestit", "Recovery Method Removed": "Palautustapa poistettu", "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)": "Käytätkö 'leivänmuruja' (kuvia huonelistan yläpuolella) vai et", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Käytätkö 'leivänmuruja' (kuvia huoneluettelon yläpuolella) vai et", "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ä erää. Haluatko ladata tiedoston vastaamatta?", "The file '%(fileName)s' failed to upload.": "Tiedoston '%(fileName)s' lataaminen ei onnistunut.", @@ -2033,7 +2033,7 @@ "Support adding custom themes": "Tue mukaututettujen teemojen lisäämistä", "Enable cross-signing to verify per-user instead of per-session (in development)": "Ota ristivarmennus käyttöön varmentaaksesi käyttäjät istuntojen sijaan (kehitysversio)", "Show rooms with unread notifications first": "Näytä ensin huoneet, joissa on lukemattomia viestejä", - "Show shortcuts to recently viewed rooms above the room list": "Näytä oikotiet viimeiseksi katsottuihin huoneisiin huonelistan yläpuolella", + "Show shortcuts to recently viewed rooms above the room list": "Näytä oikotiet viimeiseksi katsottuihin huoneisiin huoneluettelon yläpuolella", "Enable message search in encrypted rooms": "Ota viestihaku salausta käyttävissä huoneissa käyttöön", "Keep secret storage passphrase in memory for this session": "Pidä salavaraston salalause muistissa tämän istunnon ajan", "How fast should messages be downloaded.": "Kuinka nopeasti viestit pitäisi ladata.", @@ -2264,5 +2264,17 @@ "You've successfully verified your device!": "Olet onnistuneesti varmentanut laitteesi!", "You've successfully verified %(deviceName)s (%(deviceId)s)!": "Olet onnistuneesti varmentanut laitteen %(deviceName)s (%(deviceId)s)!", "You've successfully verified %(displayName)s!": "Olet varmentanut käyttäjän %(displayName)s!", - "Verified": "Varmennettu" + "Verified": "Varmennettu", + "Font size": "Fonttikoko", + "Custom font size": "Mukautettu fonttikoko", + "Size must be a number": "Koon täytyy olla luku", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Mukautetun fonttikoon täytyy olla vähintään %(min)s pt ja enintään %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Käytä kokoa väliltä %(min)s pt ja %(max)s pt", + "Appearance": "Ulkoasu", + "You can’t disable this later. Bridges & most bots won’t work yet.": "Et voi poistaa tätä käytöstä jatkossa. Sillat ja useimmat botit eivät vielä toimi.", + "Navigate up/down in the room list": "Siirry huoneluettelossa ylöspäin/alaspäin", + "Select room from the room list": "Valitse huone huoneluettelosta", + "Previous/next unread room or DM": "Edellinen/seuraava lukematon huone tai yksityisviesti", + "Previous/next room or DM": "Edellinen/seuraava huone tai yksityisviesti", + "Toggle this dialog": "Tämä valintaikkuna päälle/pois" } From 9dcfe6db6a1bc8418bcdbb61d8d45f889c519eaa Mon Sep 17 00:00:00 2001 From: XoseM Date: Fri, 22 May 2020 13:57:50 +0000 Subject: [PATCH 284/399] Translated using Weblate (Galician) Currently translated at 46.2% (1073 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 69 ++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 5b7dd05251..99d68ca943 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -264,8 +264,8 @@ "Seen by %(userName)s at %(dateTime)s": "Visto por %(userName)s as %(dateTime)s", "No rooms to show": "Sen salas que mostrar", "Unnamed room": "Sala sen nome", - "World readable": "Visible por todos", - "Guests can join": "Convidados pódense unir", + "World readable": "Visible para todas", + "Guests can join": "Convidadas pódense unir", "Save": "Gardar", "(~%(count)s results)|other": "(~%(count)s resultados)", "(~%(count)s results)|one": "(~%(count)s resultado)", @@ -671,7 +671,7 @@ "Passphrase must not be empty": "A frase de paso non pode quedar baldeira", "Export room keys": "Exportar chaves da sala", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Este proceso permítelle exportar a un ficheiro local as chaves para as mensaxes que recibiu en salas cifradas. Posteriormente permitiralle importar as chaves en outro cliente Matrix no futuro, así o cliente poderá descifrar esas mensaxes.", - "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "O ficheiro exportado permitiralle a calquera que poida lelo descifrar e cifrar mensaxes que vostede ve, así que debería ter coidado e gardalo de xeito seguro. Para axudarlle, debe introducir unha frase de paso aquí abaixo que será utilizada para cifrar os datos exportados. Só será posible importar os datos utilizando a mesma frase de paso.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "O ficheiro exportado permitiralle a calquera que poida lelo descifrar e cifrar mensaxes que ti ves, así que deberías ter coidado e gardalo de xeito seguro. Para axudarche, deberías escribir unha frase de paso aquí abaixo que será usada para cifrar os datos exportados. Só será posible importar os datos utilizando a mesma frase de paso.", "Enter passphrase": "Introduza a frase de paso", "Confirm passphrase": "Confirme a frase de paso", "Export": "Exportar", @@ -701,8 +701,8 @@ "Flair": "Popularidade", "Showing flair for these communities:": "Mostrar a popularidade destas comunidades:", "Display your community flair in rooms configured to show it.": "Mostrar a popularidade da túa comunidade nas salas configuradas para que a mostren.", - "Did you know: you can use communities to filter your Riot.im experience!": "Sabía que pode utilizar as comunidades para mellorar a súa experiencia con Riot.im!", - "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.": "Para establecer un filtro, arrastre un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Pode pulsar nun avatar no panel de filtrado en calquera momento para ver só salas e xente asociada a esa comunidade.", + "Did you know: you can use communities to filter your Riot.im experience!": "Sabías que podes usar as comunidades para filtrar a túa experiencia en Riot.im!", + "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.": "Para establecer un filtro, arrastra un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Podes premer nun avatar no panel de filtrado en calquera momento para ver só salas e xente asociada a esa comunidade.", "Deops user with given id": "Degradar o usuario con esa ID", "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Visto por %(displayName)s(%(userName)s en %(dateTime)s", "Code": "Código", @@ -711,7 +711,7 @@ "Changes made to your community name and avatar might not be seen by other users for up to 30 minutes.": "Os cambios realizados a súa comunidade name e avatar poida que non os vexan outros usuarios ate dentro de 30 minutos.", "Join this community": "Únase a esta comunidade", "Leave this community": "Deixar esta comunidade", - "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.": "Si enviou un reporte de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso do aplicativo incluíndo o seu nome de usuaria, os IDs ou alcumes das salas e grupos que visitou e os nomes de usuaria de outras persoas. Non conteñen mensaxes.", + "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.": "Se enviaches un informe de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso da aplicación incluíndo o teu nome de usuaria, os IDs ou alcumes das salas e grupos que visitaches e os nomes de usuaria de outras persoas. Non conteñen mensaxes.", "Submit debug logs": "Enviar informes de depuración", "Opens the Developer Tools dialog": "Abre o cadro de Ferramentas de desenvolvemento", "Stickerpack": "Iconas", @@ -756,7 +756,7 @@ "Please set a password!": "Por favor estableza un contrasinal!", "You have successfully set a password!": "Mudou con éxito o seu contrasinal!", "An error occurred whilst saving your email notification preferences.": "Algo fallou mentres se gardaban as súas preferencias de notificación.", - "Explore Room State": "Explorar estado da sala", + "Explore Room State": "Ollar estado da sala", "Source URL": "URL fonte", "Messages sent by bot": "Mensaxes enviadas por bot", "Filter results": "Filtrar resultados", @@ -779,7 +779,7 @@ "Developer Tools": "Ferramentas para desenvolver", "Preparing to send logs": "Preparándose para enviar informe", "Remember, you can always set an email address in user settings if you change your mind.": "Lembre que sempre poderá poñer un enderezo de correo nos axustes de usuario se cambiase de idea.", - "Explore Account Data": "Explorar datos da conta", + "Explore Account Data": "Ollar datos da conta", "All messages (noisy)": "Todas as mensaxes (alto)", "Saturday": "Sábado", "I understand the risks and wish to continue": "Entendo os riscos e desexo continuar", @@ -868,9 +868,9 @@ "Share Link to User": "Compartir a ligazón co usuario", "Share room": "Compartir sala", "Muted Users": "Usuarios silenciados", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Mellore Riot.im enviando os datos anónimos de uso. Iso suporá o emprego dunha cookie (véxase a nosa Política de Cookies).", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Mellore Riot.im enviando o uso de datos anónimo. Iso usará unha cookie.", - "Yes, I want to help!": "Si, quero axuda", + "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Axuda a mellorar Riot.im enviando os datos anónimos de uso. Usaremos unha cookie (le aquí a nosa Política de Cookies).", + "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Axuda a mellorar Riot.im enviando datos anónimos de uso. Esto usará unha cookie.", + "Yes, I want to help!": "Si, quero axudar!", "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.": "Iso fará que a súa deixe de ter uso de xeito permanente. Non poderá acceder e ninguén vai a poder volver a rexistrar esa mesma ID de usuario. Suporá que saía de todas as salas de conversas nas que estaba e eliminará os detalles da súa conta do servidores de identificación.Isto non se poderá desfacer", "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.": "Desactivando a súa conta non supón que por defecto esquezamos as súas mensaxes enviadas. Se quere que nos esquezamos das súas mensaxes, prema na caixa de embaixo.", "To continue, please enter your password:": "Para continuar introduza o seu contrasinal:", @@ -936,7 +936,7 @@ "Whether you're using Riot on a device where touch is the primary input mechanism": "Se estás conectada utilizando Riot nun dispositivo maiormente táctil", "Whether you're using Riot as an installed Progressive Web App": "Se estás a usar Riot como unha Progressive Web App instalada", "Your user agent": "User Agent do navegador", - "The information being sent to us to help make Riot better includes:": "Información que nos envías para mellorar Riot inclúe:", + "The information being sent to us to help make Riot better includes:": "A información que nos envías para mellorar Riot inclúe:", "Please install Chrome, Firefox, or Safari for the best experience.": "Instala Chrome, Firefox, ou Safari para ter unha mellor experiencia.", "Sign In or Create Account": "Conéctate ou Crea unha Conta", "Sign In": "Conectar", @@ -1042,5 +1042,48 @@ "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s cambiou o nome da sala de %(oldRoomName)s a %(newRoomName)s.", "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s actualizou esta sala.", "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s converteu en pública a sala para calquera que teña a ligazón.", - "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s fixo que a sala sexa só por convite." + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s fixo que a sala sexa só por convite.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s cambiou a regra de participación a %(rule)s", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s permite que as convidadas se unan a sala.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s non permite que as convidadas se unan a sala.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s cambiou acceso de convidada a %(rule)s", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s activou a popularidade para %(groups)s nesta sala.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s desactivou a popularidade para %(groups)s nesta sala.", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s activou a popularidade para %(newGroups)s e desactivou a popularidade para %(oldGroups)s nesta sala.", + "Capitalization doesn't help very much": "Escribir con maiúsculas non axuda moito", + "Predictable substitutions like '@' instead of 'a' don't help very much": "Substitucións predecibles como '@' no lugar de 'a' non son de gran axuda", + "Group & filter rooms by custom tags (refresh to apply changes)": "Agrupar e filtrar salas con etiquetas personalizadas (actuliza para aplicar cambios)", + "Enable Community Filter Panel": "Activar o panel de Filtro de comunidades", + "General": "Xeral", + "Discovery": "Descubrir", + "Deactivate account": "Desactivar conta", + "For help with using Riot, click here.": "Para ter axuda con Riot, preme aquí.", + "For help with using Riot, click here or start a chat with our bot using the button below.": "Se precisas axuda usando Riot, preme aquí ou inicia unha conversa co noso bot usando o botón inferior.", + "Help & About": "Axuda & Acerca de", + "Security & Privacy": "Seguridade & Privacidade", + "Where you’re logged in": "Onde estás conectada", + "Change room name": "Cambiar nome da sala", + "Roles & Permissions": "Roles & Permisos", + "Room %(name)s": "Sala %(name)s", + "Recent rooms": "Salas recentes", + "Direct Messages": "Mensaxes Directas", + "Create room": "Crear sala", + "You can use /help to list available commands. Did you mean to send this as a message?": "Podes usar axuda para ver os comandos dispoñibles. ¿Querías mellor enviar esto como unha mensaxe?", + "Error updating flair": "Fallo ao actualizar popularidade", + "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Algo fallou cando se actualizaba a popularidade da sala. Pode ser un fallo temporal ou que o servidor non o permita.", + "Enter the name of a new server you want to explore.": "Escribe o nome do novo servidor que queres explorar.", + "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.": "Se hai contexto que cres que axudaría a analizar o problema, como o que estabas a facer, ID da sala, ID da usuaria, etc., por favor inclúeo aquí.", + "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.": "Para evitar informes duplicados, mira os informes existentes primeiro (e engade un +1) ou crea un novo informe se non o atopas.", + "Command Help": "Comando Axuda", + "To help us prevent this in future, please send us logs.": "Para axudarnos a previr esto no futuro, envíanos o rexistro.", + "Help": "Axuda", + "Explore Public Rooms": "Explorar Salas Públicas", + "Explore": "Explorar", + "Filter": "Filtrar", + "Filter rooms…": "Filtrar salas…", + "%(creator)s created and configured the room.": "%(creator)s creou e configurou a sala.", + "Explore rooms": "Explorar salas", + "General failure": "Fallo xeral", + "This homeserver does not support login using email address.": "Este servidor non soporta a conexión usando enderezos de email.", + "Clear room list filter field": "Baleirar o campo do filtro de salas" } From dd8c65eba2c2b880b632c18c82324580d45b87b9 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Fri, 22 May 2020 14:12:09 +0000 Subject: [PATCH 285/399] Translated using Weblate (Lithuanian) Currently translated at 52.8% (1227 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 154 +++++++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 29 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index de8109913c..9c6a1c9a7a 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -133,7 +133,7 @@ "Reject": "Atmesti", "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Jūs turbūt juos sukonfigūravote kitoje programėlėje nei Riot. Negalite jų koreguoti Riot programėlėje, bet jie vistiek yra taikomi", "Sorry, your browser is not able to run Riot.": "Atleiskite, jūsų naršyklė negali paleisti Riot.", - "Quote": "Citata", + "Quote": "Cituoti", "Messages in group chats": "Žinutės grupės pokalbiuose", "Yesterday": "Vakar", "Error encountered (%(errorDetail)s).": "Susidurta su klaida (%(errorDetail)s).", @@ -287,14 +287,14 @@ "%(senderName)s sent an image": "%(senderName)s išsiuntė paveikslą", "%(senderName)s sent a video": "%(senderName)s išsiuntė vaizdo įrašą", "%(senderName)s uploaded a file": "%(senderName)s įkėlė failą", - "Options": "Parametrai", + "Options": "Parinktys", "Key request sent.": "Rakto užklausa išsiųsta.", "device id: ": "įrenginio id: ", "Failed to mute user": "Nepavyko nutildyti naudotoją", "Are you sure?": "Ar tikrai?", "Ignore": "Ignoruoti", "Invite": "Pakviesti", - "User Options": "Naudotojo parametrai", + "User Options": "Vartotojo parinktys", "Admin Tools": "Administratoriaus įrankiai", "Attachment": "Priedas", "Voice call": "Balso skambutis", @@ -375,8 +375,8 @@ "Sent messages will be stored until your connection has returned.": "Išsiųstos žinutės bus saugomos tol, kol atsiras ryšys.", "Active call": "Aktyvus skambutis", "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Čia daugiau nieko nėra! Ar norėtumėte pakviesti kitus ar išjungti įspėjimą apie tuščią kambarį?", - "You seem to be uploading files, are you sure you want to quit?": "Atrodo, kad jūs įkelinėjate failus, ar tikrai norite išeiti?", - "You seem to be in a call, are you sure you want to quit?": "Atrodo, kad dalyvaujate skambutyje, ar tikrai norite išeiti?", + "You seem to be uploading files, are you sure you want to quit?": "Panašu, kad jūs įkelinėjate failus, ar tikrai norite išeiti?", + "You seem to be in a call, are you sure you want to quit?": "Panašu, kad jūs dalyvaujate skambutyje, ar tikrai norite išeiti?", "Search failed": "Paieška nepavyko", "Server may be unavailable, overloaded, or search timed out :(": "Gali būti, kad serveris neprieinamas, perkrautas arba pasibaigė paieškai skirtas laikas :(", "No more results": "Daugiau nėra jokių rezultatų", @@ -399,7 +399,7 @@ "": "", "Check for update": "Tikrinti, ar yra atnaujinimų", "Reject all %(invitedRooms)s invites": "Atmesti visus %(invitedRooms)s pakvietimus", - "You may need to manually permit Riot to access your microphone/webcam": "Jums gali tekti rankiniu būdu leisti Riot prieigą prie savo mikrofono/kameros", + "You may need to manually permit Riot to access your microphone/webcam": "Jums gali tekti rankiniu būdu duoti leidimą Riot prieigai prie mikrofono/kameros", "No Audio Outputs detected": "Neaptikta jokių garso išvesčių", "No Microphones detected": "Neaptikta jokių mikrofonų", "No Webcams detected": "Neaptikta jokių kamerų", @@ -421,7 +421,7 @@ "Return to login screen": "Grįžti į prisijungimą", "Send Reset Email": "Siųsti atstatymo el. laišką", "Incorrect username and/or password.": "Neteisingas vartotojo vardas ir/arba slaptažodis.", - "Please note you are logging into the %(hs)s server, not matrix.org.": "Turėkite omenyje, kad jūs prisijungiate prie %(hs)s serverio, o ne matrix.org.", + "Please note you are logging into the %(hs)s server, not matrix.org.": "Atkreipkite dėmesį, kad jūs jungiatės prie %(hs)s serverio, o ne matrix.org.", "Failed to fetch avatar URL": "Nepavyko gauti avataro URL", "Commands": "Komandos", "Results from DuckDuckGo": "Rezultatai iš DuckDuckGo", @@ -529,7 +529,7 @@ "Confirm password": "Patvirtinkite slaptažodį", "Demote yourself?": "Pažeminti save?", "Demote": "Pažeminti", - "Share Link to User": "Dalintis nuoroda į naudotoją", + "Share Link to User": "Dalintis nuoroda į vartotoją", "Direct chats": "Privatūs pokalbiai", "The conversation continues here.": "Pokalbis tęsiasi čia.", "Jump to message": "Pereiti prie žinutės", @@ -641,7 +641,7 @@ "And %(count)s more...|other": "Ir dar %(count)s...", "Existing Call": "Esamas skambutis", "A call is already in progress!": "Skambutis jau vyksta!", - "Default": "Numatytasis", + "Default": "Paprastas vartotojas", "Restricted": "Apribotas", "Moderator": "Moderatorius", "Ignores a user, hiding their messages from you": "Ignoruoja vartotoją, slepiant nuo jūsų jo žinutes", @@ -775,7 +775,7 @@ "Invite new community members": "Pakviesti naujus bendruomenės narius", "Name or Matrix ID": "Vardas arba Matrix ID", "Identity server has no terms of service": "Tapatybės serveris neturi paslaugų teikimo sąlygų", - "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Šiam veiksmui reikalinga prieiti numatytąjį tapatybės serverį , kad patvirtinti el. pašto adresą arba telefono numerį, bet serveris neturi jokių paslaugos teikimo sąlygų.", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Šiam veiksmui reikalinga pasiekti numatytąjį tapatybės serverį , kad patvirtinti el. pašto adresą arba telefono numerį, bet serveris neturi jokių paslaugos teikimo sąlygų.", "Only continue if you trust the owner of the server.": "Tęskite tik tada, jei pasitikite serverio savininku.", "Trust": "Pasitikėti", "Failed to invite users to the room:": "Nepavyko pakviesti vartotojų į kambarį:", @@ -897,7 +897,7 @@ "Filter": "Filtruoti", "Filter rooms…": "Filtruoti kambarius…", "This room is not public. You will not be able to rejoin without an invite.": "Šis kambarys nėra viešas. Jūs negalėsite prie jo vėl prisijungti be pakvietimo.", - "Are you sure you want to leave the room '%(roomName)s'?": "Ar tikrai norite palikti kambarį %(roomName)s?", + "Are you sure you want to leave the room '%(roomName)s'?": "Ar tikrai norite išeiti iš kambario %(roomName)s?", "%(creator)s created and configured the room.": "%(creator)s sukūrė ir sukonfigūravo kambarį.", "Riot failed to get the public room list.": "Riot nepavyko gauti viešų kambarių sąrašą.", "General failure": "Bendras triktis", @@ -939,12 +939,12 @@ "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.": "Ar tikrai norite pašalinti (ištrinti) šį įvykį? Atkreipkite dėmesį į tai, kad jei jūs ištrinsite kambario pavadinimo arba temos keitimo įvykį, tai gali atšaukti patį pakeitimą.", "We recommend you change your password and recovery key in Settings immediately": "Mes rekomenduojame nedelsiant Nustatymuose pasikeisti jūsų slaptažodį ir atgavimo raktą", "Email (optional)": "El. paštas (neprivaloma)", - "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Jei jūs nenustatėte naujo paskyros atgavimo metodo, tada gali būti, kad užpuolikas bando patekti į jūsų paskyrą. Nedelsiant Nustatymuose pakeiskite savo paskyros slaptažodį ir nustatykite naują paskyros atgavimo metodą.", - "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.": "Jei jūs nepašalinote paskyros atgavimo metodo, tada gali būti, kad užpuolikas bando patekti į jūsų paskyrą. Nedelsiant Nustatymuose pakeiskite savo paskyros slaptažodį ir nustatykite naują paskyros atgavimo metodą.", + "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Jei jūs nenustatėte naujo paskyros atgavimo metodo, gali būti, kad užpuolikas bando patekti į jūsų paskyrą. Nedelsiant nustatymuose pakeiskite savo paskyros slaptažodį ir nustatykite naują atgavimo metodą.", + "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.": "Jei jūs nepašalinote paskyros atgavimo metodo, gali būti, kad užpuolikas bando patekti į jūsų paskyrą. Nedelsiant nustatymuose pakeiskite savo paskyros slaptažodį ir nustatykite naują atgavimo metodą.", "Help & About": "Pagalba ir Apie", "Direct Messages": "Privačios žinutės", "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Nustatykite adresus šiam kambariui, kad vartotojai galėtų surasti šį kambarį per jūsų serverį (%(localDomain)s)", - "Direct message": "Privati žinutė", + "Direct message": "Siųsti tiesioginę žinutę", "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s prisijungė %(count)s kartų(-us)", "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s prisijungė", "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s prisijungė %(count)s kartų(-us)", @@ -990,7 +990,7 @@ "Custom level": "Pritaikytas lygis", "Can't find this server or its room list": "Negalime rasti šio serverio arba jo kambarių sąrašo", "Matrix rooms": "Matrix kambariai", - "Recently Direct Messaged": "Neseniai siųsta privati žinutė", + "Recently Direct Messaged": "Neseniai tiesiogiai susirašyta", "Command Help": "Komandų pagalba", "Help": "Pagalba", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Sukurkite bendruomenę, kad kartu sugrupuotumėte vartotojus ir kambarius! Sukurkite pagrindinį puslapį, kad pažymėtumėte savo vietą Matrix visatoje.", @@ -1064,7 +1064,7 @@ "Server Name": "Serverio Pavadinimas", "Other servers": "Kiti serveriai", "Add room": "Sukurti kambarį", - "Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Slaptažodžio keitimas ištrins visų jūsų seansų šifravimo raktus, todėl nebebus galima perskaityti užšifruotos pokalbių istorijos. Nustatykite Raktų Atsarginę Kopiją arba eksportuokite savo kambarių raktus iš kito seanso prieš atstatydami slaptažodį.", + "Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Slaptažodžio keitimas ištrins visų jūsų seansų šifravimo raktus, todėl nebebus galima perskaityti užšifruotos pokalbių istorijos. Sukurkite atsarginę raktų kopiją arba eksportuokite savo kambarių raktus iš kito seanso prieš atstatydami slaptažodį.", "Set a display name:": "Nustatyti rodomą vardą:", "Secure your encryption keys with a passphrase. For maximum security this should be different to your account password:": "Apsaugokite savo šifravimo raktus slaptafraze. Maksimaliam saugumui užtikrinti ji turi skirtis nuo jūsų paskyros slaptažodžio:", "Enter a passphrase": "Įveskite slaptafrazę", @@ -1082,13 +1082,13 @@ "Syncing...": "Sinchronizuojama...", "Signing In...": "Prijungiama...", "If you've joined lots of rooms, this might take a while": "Jei esate prisijungę prie daug kambarių, tai gali užtrukti", - "Without completing security on this session, it won’t have access to encrypted messages.": "Neužbaigus saugumo šioje sesijoje, ji neturės priėjimo prie šifruotų žinučių.", + "Without completing security on this session, it won’t have access to encrypted messages.": "Neužbaigus saugumo šiame seanse, jis neturės prieigos prie šifruotų žinučių.", "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Nustatykite atgavimo slaptafrazę, kad apsaugotumėte šifruotą informaciją ir atgautumėte ją jei atsijungsite. Ji turi skirtis nuo jūsų paskyros slaptažodžio:", "Enter a recovery passphrase": "Įveskite atgavimo slaptafrazę", "Back up encrypted message keys": "Padaryti atsargines šifruotų žinučių raktų kopijas", "Set up with a recovery key": "Nustatyti su atgavimo raktu", "Enter your recovery passphrase a second time to confirm it.": "Įveskite atgavimo slaptafrazę antrą kartą, kad ją patvirtintumėte.", - "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Jūsų atgavimo raktas yra atsarginė saugumo priemonė - jūs galite jį naudoti priėjimo prie jūsų šifruotų žinučių atgavimui, jei pamiršite savo atgavimo slaptafrazę.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Jūsų atgavimo raktas yra atsarginė saugumo priemonė - jūs galite jį naudoti prieigos prie jūsų šifruotų žinučių atgavimui, jei pamiršite savo atgavimo slaptafrazę.", "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Laikykite šio rakto kopiją saugioje vietoje, pavyzdžiui slaptažodžių tvarkyklėje arba seife.", "Your recovery key": "Jūsų atgavimo raktas", "Copy": "Kopijuoti", @@ -1105,9 +1105,9 @@ "Not now": "Ne dabar", "Don't ask me again": "Daugiau neklausti", "Send as message": "Siųsti kaip žinutę", - "Messages in this room are end-to-end encrypted.": "Žinutės šiame kambaryje yra užšifruotos visapusiu šifravimu.", - "Messages in this room are not end-to-end encrypted.": "Žinutės šiame kambaryje nėra užšifruotos visapusiu šifravimu.", - "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Žinutės šiame kambaryje yra užšifruotos visapusiu šifravimu. Sužinokite daugiau ir patvirtinkite šį vartotoją jų profilyje.", + "Messages in this room are end-to-end encrypted.": "Žinutės šiame kambaryje yra visapusiškai užšifruotos.", + "Messages in this room are not end-to-end encrypted.": "Žinutės šiame kambaryje nėra visapusiškai užšifruotos.", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Žinutės šiame kambaryje yra visapusiškai užšifruotos. Sužinokite daugiau ir patvirtinkite vartotojus jų profilyje.", "Confirm Removal": "Patvirtinkite pašalinimą", "Manually export keys": "Eksportuoti raktus rankiniu būdu", "Send a Direct Message": "Siųsti tiesioginę žinutę", @@ -1127,15 +1127,15 @@ "Homeserver URL": "Serverio URL", "Homeserver URL does not appear to be a valid Matrix homeserver": "Serverio adresas neatrodo esantis tinkamas Matrix serveris", "This homeserver does not support login using email address.": "Šis serveris nepalaiko prisijungimo naudojant el. pašto adresą.", - "Review Sessions": "Peržiūrėti sesijas", + "Review Sessions": "Peržiūrėti seansus", "Setting up keys": "Raktų nustatymas", "Review where you’re logged in": "Peržiūrėkite kur esate prisijungę", - "Verify all your sessions to ensure your account & messages are safe": "Patvirtinkite visas savo sesijas, kad užtikrintumėte savo paskyros ir žinučių saugumą", + "Verify all your sessions to ensure your account & messages are safe": "Patvirtinkite visus savo seansus, kad užtikrintumėte savo paskyros ir žinučių saugumą", "Review": "Peržiūrėti", "Message deleted": "Žinutė ištrinta", "Message deleted by %(name)s": "Žinutė, ištrinta %(name)s", "Warning: You should only do this on a trusted computer.": "Įspėjimas: Tai atlikite tik saugiame kompiuteryje.", - "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Prieikite prie savo saugių žinučių istorijos ir kryžminio parašo kitų sesijų patvirtinimui tapatybės įvesdami savo atgavimo slaptafrazę.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Pasiekite savo saugių žinučių istoriją ir kryžminio parašo tapatybę, naudojamą kitų seansų patvirtinimui, įvesdami savo atgavimo slaptafrazę.", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus.", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus", "Confirm your identity by entering your account password below.": "Patvirtinkite savo tapatybę žemiau įvesdami savo paskyros slaptažodį.", @@ -1154,16 +1154,112 @@ "You can now verify your other devices, and other users to keep your chats safe.": "Jūs dabar galite patvirtinti kitus savo įrenginius ir kitus vartotojus, kad jūsų pokalbiai būtų saugūs.", "Confirm recovery passphrase": "Patvirtinkite atgavimo slaptafrazę", "You're done!": "Atlikta!", - "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Slaptažodžio keitimas šiuo metu anuliuos visapusio šifravimo ranktus visose sesijose, tad šifruotų pokalbių istorija taps neperskaitoma, nebent jūs eksportuosite savo kambarių raktus ir po to importuosite juos atgal. Ateityje ši funkcija bus pataisyta.", - "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Jūsų slaptažodis buvo sėkmingai pakeistas. Jūs nematysite pranešimų kitose sesijose kol iš naujo prie jų neprisijungsite", + "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Pakeitus slaptažodį šiuo metu, visuose seansuose bus anuliuoti visapusio šifravimo raktai, tad šifruotų pokalbių istorija taps neperskaitoma, nebent jūs eksportuosite savo kambarių raktus ir po to importuosite juos atgal. Ateityje ši funkcija bus pataisyta.", + "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Jūsų slaptažodis buvo sėkmingai pakeistas. Jūs kituose seansuose negausite pranešimų, kol iš naujo prie jų neprisijungsite", "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "El. laiškas buvo išsiųstas į %(emailAddress)s. Kai paspausite jame esančią nuorodą, tada spauskite žemiau.", "Your password has been reset.": "Jūsų slaptažodis buvo iš naujo nustatytas.", - "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų sesijų ir nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, prisijunkite iš naujo kiekviename įrenginyje.", + "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų seansų ir toliau nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, iš naujo prisijunkite kiekviename įrenginyje.", "Show more": "Rodyti daugiau", - "Log in to your new account.": "Prisijunkite į savo naują paskyrą.", + "Log in to your new account.": "Prisijunkite prie naujos paskyros.", "Registration Successful": "Registracija sėkminga", "Welcome to %(appName)s": "Sveiki prisijungę į %(appName)s", "Liberate your communication": "Išlaisvinkite savo bendravimą", "Explore Public Rooms": "Žvalgyti viešus kambarius", - "Create a Group Chat": "Sukurti grupės pokalbį" + "Create a Group Chat": "Sukurti grupės pokalbį", + "New login. Was this you?": "Naujas prisijungimas. Ar tai jūs?", + "%(senderName)s placed a voice call.": "%(senderName)s pradėjo balso skambutį.", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s pradėjo vaizdo skambutį. (nepalaikoma šios naršyklės)", + "%(senderName)s placed a video call.": "%(senderName)s pradėjo vaizdo skambutį.", + "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s pradėjo vaizdo skambutį. (nepalaikoma šios naršyklės)", + "Verify your other session using one of the options below.": "Patvirtinkite savo kitą seansą naudodami vieną iš žemiau esančių parinkčių.", + "Messages containing @room": "Žinutės, kuriose yra @kambarys", + "Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…": "Laukiama, kol jūsų kitas seansas, %(deviceName)s (%(deviceId)s), patvirtins…", + "Waiting for your other session to verify…": "Laukiama, kol jūsų kitas seansas patvirtins…", + "To be secure, do this in person or use a trusted way to communicate.": "Norėdami būti saugūs, darykite tai asmeniškai arba naudodamiesi patikimu bendravimo būdu.", + "Verify": "Patvirtinti", + "Verify the new login accessing your account: %(name)s": "Patvirtinkite naują prisijungimą prie jūsų paskyros: %(name)s", + "Confirm deleting these sessions": "Patvirtinkite šių seansų ištrinimą", + "Delete sessions|other": "Ištrinti seansus", + "Delete sessions|one": "Ištrinti seansą", + "Delete %(count)s sessions|other": "Ištrinti %(count)s seansus(-ų)", + "Delete %(count)s sessions|one": "Ištrinti %(count)s seansą", + "ID": "ID", + "Restore from Backup": "Atkurti iš atsarginės kopijos", + "Flair": "Ženkliukai", + "Access Token:": "Prieigos talonas:", + "Preferences": "Parinktys", + "Cryptography": "Kriptografija", + "Security & Privacy": "Saugumas ir Privatumas", + "Voice & Video": "Garsas ir Vaizdas", + "Enable room encryption": "Įgalinti kambario šifravimą", + "Enable encryption?": "Įgalinti šifravimą?", + "Encryption": "Šifravimas", + "Once enabled, encryption cannot be disabled.": "Įjungus šifravimą jo nebus galima išjungti.", + "Who can access this room?": "Kas turi prieigą prie šio kambario?", + "Join as voice or video.": "Prisijungti kaip balsas arba vaizdas.", + "Deactivate user?": "Deaktyvuoti vartotoją?", + "Deactivate user": "Deaktyvuoti vartotoją", + "Failed to deactivate user": "Nepavyko deaktyvuoti vartotojo", + "Sessions": "Seansai", + "Try again later, or ask a room admin to check if you have access.": "Pabandykite vėliau arba paprašykite kambario administratoriaus patikrinti, ar turite prieigą.", + "%(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.": "Bandant patekti į kambarį buvo gauta klaida: %(errcode)s. Jei manote, kad matote šį pranešimą per klaidą, prašome apie ją pranešti.", + "Error updating flair": "Klaida atnaujinant ženkliuką", + "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Įvyko klaida atnaujinant ženkliukus šiam kambariui. Serveris gali jų neleisti arba įvyko laikina klaida.", + "Showing flair for these communities:": "Ženkliukai rodomi šioms bendruomenėms:", + "This room is not showing flair for any communities": "Šis kambarys nerodo ženkliukų jokioms bendruomenėms", + "Waiting for you to accept on your other session…": "Laukiama kol jūs priimsite savo kitame seanse…", + "Start Verification": "Pradėti patvirtinimą", + "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.": "Šifruotuose kambariuose jūsų žinutės yra apsaugotos ir tik jūs ir gavėjas turite unikalius raktus joms atrakinti.", + "Verify User": "Patvirtinti vartotoją", + "For extra security, verify this user by checking a one-time code on both of your devices.": "Dėl papildomo saugumo patvirtinkite šį vartotoją patikrindami vienkartinį kodą abiejuose jūsų įrenginiuose.", + "%(count)s verified sessions|other": "%(count)s patvirtintų seansų", + "Hide verified sessions": "Slėpti patvirtintus seansus", + "%(count)s sessions|other": "%(count)s seansai(-ų)", + "Hide sessions": "Slėpti seansus", + "%(role)s in %(roomName)s": "%(role)s kambaryje %(roomName)s", + "Security": "Saugumas", + "Verify by scanning": "Patvirtinti skenuojant", + "Verify all users in a room to ensure it's secure.": "Patvirtinkite visus vartotojus kambaryje, kad užtikrintumėte jo saugumą.", + "You cancelled verification on your other session.": "Jūs atšaukėte patvirtinimą kitame savo seanse.", + "%(displayName)s cancelled verification.": "%(displayName)s atšaukė patvirtinimą.", + "You cancelled verification.": "Jūs atšaukėte patvirtinimą.", + "Verification cancelled": "Patvirtinimas atšauktas", + "Encryption enabled": "Šifravimas įgalintas", + "Encryption not enabled": "Šifravimas neįgalintas", + "Display your community flair in rooms configured to show it.": "Rodyti savo bendruomenės ženkliukus kambariuose, kuriuose nustatytas jų rodymas.", + "You're not currently a member of any communities.": "Jūs šiuo metu nesate jokios bendruomenės narys.", + "More options": "Daugiau parinkčių", + "Are you sure you want to remove %(serverName)s": "Ar tikrai norite pašalinti %(serverName)s", + "Enable end-to-end encryption": "Įgalinti visapusį šifravimą", + "You can’t disable this later. Bridges & most bots won’t work yet.": "Jūs negalėsite vėliau to išjungti. Tiltai ir dauguma bot'ų dar nėra palaikomi.", + "Are you sure you want to deactivate your account? This is irreversible.": "Ar tikrai norite deaktyvuoti savo paskyrą? Tai yra negrįžtama.", + "Verify session": "Patvirtinti seansą", + "Verify by comparing a short text string.": "Patvirtinkite palygindami trumpą teksto eilutę.", + "To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Norėdami patvirtinti, kad šis seansas yra patikimas, susisiekite su jo savininku kokiu nors kitu būdu (pvz.: asmeniškai arba telefono skambučiu) ir paklauskite ar jų Vartotojo Nustatymuose matomas šio seanso raktas sutampa su raktu esančiu žemiau:", + "Start verification": "Pradėti patvirtinimą", + "Are you sure you want to sign out?": "Ar tikrai norite atsijungti?", + "Use this session to verify your new one, granting it access to encrypted messages:": "Panaudoti šį seansą naujo patvirtinimui, suteikant jam prieigą prie šifruotų žinučių:", + "If you didn’t sign in to this session, your account may be compromised.": "Jei jūs nesijungėte prie šios sesijos, jūsų paskyra gali būti sukompromituota.", + "This wasn't me": "Tai buvau ne aš", + "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Jei jūs susidūrėte su klaidomis arba norėtumėte palikti atsiliepimą, praneškite mums GitHub'e.", + "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.": "Tam, kad būtų išvengta pasikartojančių problemų, pirmiausia peržiūrėkite esamas problemas (ir pridėkite +1) arba, jei nerandate, sukurkite naują svarstomą problemą.", + "Report bugs & give feedback": "Pranešti apie klaidas ir palikti atsiliepimą", + "Report Content to Your Homeserver Administrator": "Pranešti apie turinį serverio administratoriui", + "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Pranešant apie šią netinkamą žinutę, serverio administratoriui bus nusiųstas unikalus 'įvykio ID'. Jei žinutės šiame kambaryje yra šifruotos, serverio administratorius negalės perskaityti žinutės teksto ar peržiūrėti failų arba paveikslėlių.", + "Send report": "Siųsti pranešimą", + "Unknown sessions": "Nežinomi seansai", + "Verify other session": "Patvirtinti kitą seansą", + "Are you sure you want to reject the invitation?": "Ar tikrai norite atšaukti pakvietimą?", + "Share Permalink": "Dalintis nuoroda", + "Report Content": "Pranešti", + "Nice, strong password!": "Puiku, stiprus slaptažodis!", + "Old cryptography data detected": "Aptikti seni kriptografijos duomenys", + "Verify this login": "Patvirtinti šį prisijungimą", + "Registration has been disabled on this homeserver.": "Registracija šiame serveryje išjungta.", + "You can now close this window or log in to your new account.": "Jūs galite uždaryti šį langą arba prisijungti į savo naują paskyrą.", + "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Patvirtinkite savo tapatybę verifikuodami šį prisijungimą viename iš kitų jūsų seansų, suteikdami jam prieigą prie šifruotų žinučių.", + "This requires the latest Riot on your other devices:": "Tam reikia naujausios Riot versijos kituose jūsų įrenginiuose:", + "or another cross-signing capable Matrix client": "arba kitas kryžminį parašą palaikantis Matrix klientas", + "Use Recovery Passphrase or Key": "Naudoti atgavimo slaptafrazę arba raktą", + "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Atnaujinkite šį seansą, kad jam būtų leista patvirtinti kitus seansus, suteikiant jiems prieigą prie šifruotų žinučių ir juos pažymint kaip patikimus kitiems vartotojams." } From f6e45a040559dac5ebc35b7e84dd7303a171b757 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Sat, 23 May 2020 20:55:27 +0000 Subject: [PATCH 286/399] Translated using Weblate (Lithuanian) Currently translated at 55.2% (1281 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 75 ++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 9c6a1c9a7a..661732dd81 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -103,7 +103,7 @@ "Search": "Ieškoti", "You must specify an event type!": "Privalote nurodyti įvykio tipą!", "(HTTP status %(httpStatus)s)": "(HTTP būsena %(httpStatus)s)", - "Failed to forget room %(errCode)s": "Nepavyko pašalinti pokalbių kambario %(errCode)s", + "Failed to forget room %(errCode)s": "Nepavyko pamiršti kambario %(errCode)s", "What's New": "Kas naujo", "Wednesday": "Trečiadienis", "Send": "Siųsti", @@ -355,9 +355,9 @@ "Email address": "El. pašto adresas", "Remove from community": "Pašalinti iš bendruomenės", "Remove this user from community?": "Pašalinti šį vartotoją iš bendruomenės?", - "Failed to remove user from community": "Nepavyko pašalinti naudotoją iš bendruomenės", + "Failed to remove user from community": "Nepavyko pašalinti vartotojo iš bendruomenės", "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Ar tikrai norite pašalinti \"%(roomName)s\" iš %(groupId)s?", - "Failed to remove room from community": "Nepavyko pašalinti kambarį iš bendruomenės", + "Failed to remove room from community": "Nepavyko pašalinti kambario iš bendruomenės", "Failed to remove '%(roomName)s' from %(groupId)s": "Nepavyko pašalinti \"%(roomName)s\" iš %(groupId)s", "Something went wrong!": "Kažkas nutiko!", "Visibility in Room List": "Matomumas kambarių sąraše", @@ -367,7 +367,7 @@ "Allow": "Leisti", "Delete Widget": "Ištrinti valdiklį", "Delete widget": "Ištrinti valdiklį", - "Failed to remove widget": "Nepavyko pašalinti valdiklį", + "Failed to remove widget": "Nepavyko pašalinti valdiklio", "Scroll to bottom of page": "Slinkti į puslapio apačią", "%(count)s of your messages have not been sent.|other": "Kai kurios iš jūsų žinučių nebuvo išsiųstos.", "%(count)s of your messages have not been sent.|one": "Jūsų žinutė nebuvo išsiųsta.", @@ -395,7 +395,7 @@ "Light theme": "Šviesi tema", "Dark theme": "Tamsi tema", "Success": "Pavyko", - "Unable to remove contact information": "Nepavyko pašalinti kontaktinę informaciją", + "Unable to remove contact information": "Nepavyko pašalinti kontaktinės informacijos", "": "", "Check for update": "Tikrinti, ar yra atnaujinimų", "Reject all %(invitedRooms)s invites": "Atmesti visus %(invitedRooms)s pakvietimus", @@ -624,7 +624,7 @@ "Please contact your service administrator to get this limit increased.": "Norėdami padidinti šį limitą, susisiekite su savo paslaugų administratoriumi.", "This homeserver has hit its Monthly Active User limit so some users will not be able to log in.": "Šis namų serveris pasiekė savo mėnesinį aktyvių naudotojų limitą, taigi, kai kurie naudotojai negalės prisijungti.", "This homeserver has exceeded one of its resource limits so some users will not be able to log in.": "Šis namų serveris viršijo vieno iš savo išteklių limitą, taigi, kai kurie naudotojai negalės prisijungti.", - "An error ocurred whilst trying to remove the widget from the room": "Įvyko klaida, bandant pašalinti valdiklį iš kambario", + "An error ocurred whilst trying to remove the widget from the room": "Bandant pašalinti valdiklį iš kambario įvyko klaida", "Blacklist": "Blokuoti", "Unblacklist": "Atblokuoti", "Verify...": "Patvirtinti...", @@ -659,7 +659,7 @@ "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s nustatė pagrindinį šio kambario adresą į %(address)s.", "%(senderName)s removed the main address for this room.": "%(senderName)s pašalino pagrindinį šio kambario adresą.", "Disinvite": "Atšaukti pakvietimą", - "Disinvite this user?": "Atšaukti pakvietimą šiam naudotojui?", + "Disinvite this user?": "Atšaukti pakvietimą šiam vartotojui?", "Unknown for %(duration)s": "Nežinoma jau %(duration)s", "Unable to load! Check your network connectivity and try again.": "Nepavyko įkelti! Patikrinkite savo tinklo ryšį ir bandykite dar kartą.", "%(targetName)s joined the room.": "%(targetName)s prisijungė prie kambario.", @@ -700,7 +700,7 @@ "Publish this room to the public in %(domain)s's room directory?": "Paskelbti šį kambarį į viešąjį %(domain)s kambarių katalogą?", "Start authentication": "Pradėti tapatybės nustatymą", "Failed to load group members": "Nepavyko įkelti grupės dalyvių", - "Manage Integrations": "Tvarkyti integracijas", + "Manage Integrations": "Valdyti integracijas", "Matrix Room ID": "Matrix kambario ID", "That doesn't look like a valid email address": "Tai nepanašu į teisingą el. pašto adresą", "Preparing to send logs": "Ruošiamasi išsiųsti žurnalus", @@ -740,7 +740,7 @@ "Your Communities": "Jūsų bendruomenės", "Create a new community": "Sukurti naują bendruomenę", "You have no visible notifications": "Jūs neturite matomų pranešimų", - "Failed to perform homeserver discovery": "Nepavyko atlikti namų serverio aptikimo", + "Failed to perform homeserver discovery": "Nepavyko atlikti serverio radimo", "Error: Problem communicating with the given homeserver.": "Klaida: Problemos susisiekiant su nurodytu namų serveriu.", "This server does not support authentication with a phone number.": "Šis serveris nepalaiko tapatybės nustatymo telefono numeriu.", "Great! This passphrase looks strong enough.": "Puiku! Ši slapta frazė atrodo pakankamai stipri.", @@ -1115,7 +1115,7 @@ "Go back to set it again.": "Grįžti atgal, kad nustatyti iš naujo.", "Click the button below to confirm adding this email address.": "Paspauskite mygtuką žemiau, kad patvirtintumėte šio el. pašto pridėjimą.", "Add an email address to configure email notifications": "Pridėkite el. pašto adresą, kad nustatytumėte el. pašto pranešimus", - "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Prieš atsijungiant rekomenduojame iš identiteto serverio pašalinti savo el. pašto adresus ir telefono numerius.", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Rekomenduojame, prieš atsijungiant, iš tapatybės serverio pašalinti savo el. pašto adresus ir telefono numerius.", "Email addresses": "El. pašto adresai", "Account management": "Paskyros valdymas", "Deactivating your account is a permanent action - be careful!": "Paskyros deaktyvavimas yra neatšaukiamas veiksmas - būkite atsargūs!", @@ -1261,5 +1261,58 @@ "This requires the latest Riot on your other devices:": "Tam reikia naujausios Riot versijos kituose jūsų įrenginiuose:", "or another cross-signing capable Matrix client": "arba kitas kryžminį parašą palaikantis Matrix klientas", "Use Recovery Passphrase or Key": "Naudoti atgavimo slaptafrazę arba raktą", - "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Atnaujinkite šį seansą, kad jam būtų leista patvirtinti kitus seansus, suteikiant jiems prieigą prie šifruotų žinučių ir juos pažymint kaip patikimus kitiems vartotojams." + "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Atnaujinkite šį seansą, kad jam būtų leista patvirtinti kitus seansus, suteikiant jiems prieigą prie šifruotų žinučių ir juos pažymint kaip patikimus kitiems vartotojams.", + "Use Single Sign On to continue": "Norėdami tęsti naudokite Vieno Prisijungimo sistemą", + "Confirm adding this email address by using Single Sign On to prove your identity.": "Patvirtinkite šio el. pašto adreso pridėjimą naudodami Vieno Prisijungimo sistemą, patvirtinančią jūsų tapatybę.", + "Single Sign On": "Vieno Prisijungimo sistema", + "Confirm adding email": "Patvirtinkite el. pašto pridėjimą", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Patvirtinkite šio telefono numerio pridėjimą naudodami Vieno Prisijungimo sistemą, patvirtinančią jūsų tapatybę.", + "Confirm adding phone number": "Patvirtinkite telefono numerio pridėjimą", + "Click the button below to confirm adding this phone number.": "Paspauskite žemiau esantį mygtuką, kad patvirtintumėte šio numerio pridėjimą.", + "The version of Riot": "Riot versija", + "Match system theme": "Suderinti su sistemos tema", + "Identity Server URL must be HTTPS": "Tapatybės serverio URL privalo būti HTTPS", + "Not a valid Identity Server (status code %(code)s)": "Netinkamas tapatybės serveris (statuso kodas %(code)s)", + "Could not connect to Identity Server": "Nepavyko prisijungti prie tapatybės serverio", + "Disconnect from the identity server and connect to instead?": "Atsijungti nuo tapatybės serverio ir jo vietoje prisijungti prie ?", + "Terms of service not accepted or the identity server is invalid.": "Nesutikta su paslaugų teikimo sąlygomis arba tapatybės serveris yra klaidingas.", + "The identity server you have chosen does not have any terms of service.": "Jūsų pasirinktas tapatybės serveris neturi jokių paslaugų teikimo sąlygų.", + "Disconnect identity server": "Atjungti tapatybės serverį", + "Disconnect from the identity server ?": "Atsijungti nuo tapatybės serverio ?", + "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Prieš atsijungdami jūs turėtumėte pašalinti savo asmeninius duomenis iš tapatybės serverio . Deja, tapatybės serveris šiuo metu yra išjungtas arba nepasiekiamas.", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "patikrinkite ar tarp jūsų naršyklės įskiepių nėra nieko kas galėtų blokuoti tapatybės serverį (pavyzdžiui \"Privacy Badger\")", + "contact the administrators of identity server ": "susisiekite su tapatybės serverio administratoriais", + "You are still sharing your personal data on the identity server .": "Jūs vis dar dalijatės savo asmeniniais duomenimis tapatybės serveryje .", + "Identity Server (%(server)s)": "Tapatybės serveris (%(server)s)", + "Enter a new identity server": "Pridėkite naują tapatybės serverį", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Naudokite integracijų valdiklį (%(serverName)s) botų, valdiklių ir lipdukų valdymui.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Naudokite integracijų valdiklį botų, valdiklių ir lipdukų valdymui.", + "Manage integrations": "Valdyti integracijas", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integracijų valdikliai gauna konfigūracijos duomenis ir jūsų vardu gali keisti valdiklius, siųsti kambario pakvietimus ir nustatyti galios lygius.", + "Invalid theme schema.": "Klaidinga temos schema.", + "Error downloading theme information.": "Klaida parsisiunčiant temos informaciją.", + "Theme added!": "Tema pridėta!", + "Custom theme URL": "Pasirinktinės temos URL", + "Add theme": "Pridėti temą", + "Theme": "Tema", + "Phone numbers": "Telefono numeriai", + "Language and region": "Kalba ir regionas", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Sutikite su tapatybės serverio (%(serverName)s) paslaugų teikimo sąlygomis, kad leistumėte kitiems rasti jus pagal el. pašto adresą ar telefono numerį.", + "Discovery": "Radimas", + "Discovery options will appear once you have added an email above.": "Radimo parinktys atsiras jums aukščiau pridėjus el. pašto adresą.", + "Unable to revoke sharing for phone number": "Neina atšaukti telefono numerio bendrinimo", + "Unable to share phone number": "Neina bendrinti telefono numerio", + "Unable to verify phone number.": "Neina patvirtinti telefono numerio.", + "Discovery options will appear once you have added a phone number above.": "Radimo parinktys atsiras jums aukščiau pridėjus telefono numerį.", + "Phone Number": "Telefono Numeris", + "Room Topic": "Kambario Tema", + "Your theme": "Jūsų tema", + "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Valdiklio ištrinimas pašalina jį visiems kambaryje esantiems vartotojams. Ar tikrai norite ištrinti šį valdiklį?", + "Enable 'Manage Integrations' in Settings to do this.": "Įgalinkite 'Valdyti integracijas' nustatymuose, kad tai atliktumėte.", + "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Jūsų Riot neleidžia jums naudoti integracijų valdiklio tam atlikti. Susisiekite su administratoriumi.", + "Enter phone number (required on this homeserver)": "Įveskite telefono numerį (privaloma šiame serveryje)", + "Doesn't look like a valid phone number": "Tai nepanašu į veikiantį telefono numerį", + "Invalid homeserver discovery response": "Klaidingas serverio radimo atsakas", + "Invalid identity server discovery response": "Klaidingas tapatybės serverio radimo atsakas", + "The phone number entered looks invalid": "Įvestas telefono numeris atrodo klaidingas" } From 490a1041088675e5778143c8906e8946173b63f5 Mon Sep 17 00:00:00 2001 From: Domant3lis Date: Sat, 23 May 2020 23:31:57 +0000 Subject: [PATCH 287/399] Translated using Weblate (Lithuanian) Currently translated at 55.2% (1281 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 661732dd81..905ac59c0d 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1314,5 +1314,6 @@ "Doesn't look like a valid phone number": "Tai nepanašu į veikiantį telefono numerį", "Invalid homeserver discovery response": "Klaidingas serverio radimo atsakas", "Invalid identity server discovery response": "Klaidingas tapatybės serverio radimo atsakas", - "The phone number entered looks invalid": "Įvestas telefono numeris atrodo klaidingas" + "The phone number entered looks invalid": "Įvestas telefono numeris atrodo klaidingas", + "Double check that your server supports the room version chosen and try again.": "Patikrinkite ar jūsų serveris palaiko pasirinktą kambario versiją ir bandykite iš naujo." } From 5e00481639ddd2505f95db3722cd97e2c67ba385 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 13:08:29 +0100 Subject: [PATCH 288/399] Fix sentMessageAndIsAlone by dispatching `message_sent` more consistently Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/emojipicker/ReactionPicker.js | 2 ++ src/components/views/messages/ReactionsRowButton.js | 2 ++ src/components/views/rooms/EditMessageComposer.js | 1 + src/components/views/rooms/SendMessageComposer.js | 1 + 4 files changed, 6 insertions(+) diff --git a/src/components/views/emojipicker/ReactionPicker.js b/src/components/views/emojipicker/ReactionPicker.js index 96894e18d2..6f8cc46c40 100644 --- a/src/components/views/emojipicker/ReactionPicker.js +++ b/src/components/views/emojipicker/ReactionPicker.js @@ -18,6 +18,7 @@ import React from 'react'; import PropTypes from "prop-types"; import EmojiPicker from "./EmojiPicker"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import dis from "../../../dispatcher/dispatcher"; class ReactionPicker extends React.Component { static propTypes = { @@ -105,6 +106,7 @@ class ReactionPicker extends React.Component { "key": reaction, }, }); + dis.dispatch({action: "message_sent"}); return true; } } diff --git a/src/components/views/messages/ReactionsRowButton.js b/src/components/views/messages/ReactionsRowButton.js index a7ff7dce96..09824cd315 100644 --- a/src/components/views/messages/ReactionsRowButton.js +++ b/src/components/views/messages/ReactionsRowButton.js @@ -22,6 +22,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; +import dis from "../../../dispatcher/dispatcher"; export default class ReactionsRowButton extends React.PureComponent { static propTypes = { @@ -60,6 +61,7 @@ export default class ReactionsRowButton extends React.PureComponent { "key": content, }, }); + dis.dispatch({action: "message_sent"}); } }; diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index 88ed76f118..b70ef6255c 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -190,6 +190,7 @@ export default class EditMessageComposer extends React.Component { const roomId = editedEvent.getRoomId(); this._cancelPreviousPendingEdit(); this.context.sendMessage(roomId, editContent); + dis.dispatch({action: "message_sent"}); } // close the event editing and focus composer diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 5ea979a8ef..b81e004943 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -312,6 +312,7 @@ export default class SendMessageComposer extends React.Component { event: null, }); } + dis.dispatch({action: "message_sent"}); } this.sendHistoryManager.save(this.model); From 02ddda587b7354f90c14e082be3170117606ef6c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 14:11:25 +0100 Subject: [PATCH 289/399] Fix useEventEmitter to support passing all callback arguments Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/hooks/useEventEmitter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useEventEmitter.js b/src/hooks/useEventEmitter.js index 7adc6ef2e3..6a758fb108 100644 --- a/src/hooks/useEventEmitter.js +++ b/src/hooks/useEventEmitter.js @@ -32,7 +32,7 @@ export const useEventEmitter = (emitter, eventName, handler) => { if (!emitter) return; // Create event listener that calls handler function stored in ref - const eventListener = event => savedHandler.current(event); + const eventListener = (...args) => savedHandler.current(...args); // Add event listener emitter.on(eventName, eventListener); From cf7acded501e6fb34f165de0e6ae721f0a8edcfc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 14:12:16 +0100 Subject: [PATCH 290/399] Simply BaseAvatar hooks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/avatars/BaseAvatar.js | 38 ++++++++-------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 4fd614fbf7..d7a2aa14e5 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -30,54 +30,44 @@ const useImageUrl = ({url, urls, idName, name, defaultToInitialLetter}) => { const [imageUrls, setUrls] = useState([]); const [urlsIndex, setIndex] = useState(); - const onError = () => { - const nextIndex = urlsIndex + 1; - if (nextIndex < imageUrls.length) { - // try the next one - setIndex(nextIndex); - } - }; - - const defaultImageUrl = useMemo(() => AvatarLogic.defaultAvatarUrlForString(idName || name), [idName, name]); + const onError = useCallback(() => { + setIndex(i => i + 1); // try the next one + }, []); + const memoizedUrls = useMemo(() => urls, [JSON.stringify(urls)]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { // work out the full set of urls to try to load. This is formed like so: - // imageUrls: [ props.url, ...props.urls, default image ] + // imageUrls: [ props.url, ...props.urls ] let _urls = []; if (!SettingsStore.getValue("lowBandwidth")) { - _urls = urls || []; + _urls = memoizedUrls || []; if (url) { _urls.unshift(url); // put in urls[0] } } - if (defaultToInitialLetter) { - _urls.push(defaultImageUrl); // lowest priority - } - // deduplicate URLs _urls = Array.from(new Set(_urls)); setIndex(0); setUrls(_urls); - }, [url, ...(urls || [])]); // eslint-disable-line react-hooks/exhaustive-deps + }, [url, memoizedUrls]); // eslint-disable-line react-hooks/exhaustive-deps const cli = useContext(MatrixClientContext); const onClientSync = useCallback((syncState, prevState) => { // Consider the client reconnected if there is no error with syncing. // This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP. const reconnected = syncState !== "ERROR" && prevState !== syncState; - if (reconnected && urlsIndex > 0 ) { // Did we fall back? - // Start from the highest priority URL again - setIndex(0); + if (reconnected) { + setIndex(i => i > 0 ? 0 : i); } - }, [urlsIndex]); + }, []); useEventEmitter(cli, "sync", onClientSync); const imageUrl = imageUrls[urlsIndex]; - return [imageUrl, imageUrl === defaultImageUrl, onError]; + return [imageUrl, onError]; }; const BaseAvatar = (props) => { @@ -96,9 +86,9 @@ const BaseAvatar = (props) => { ...otherProps } = props; - const [imageUrl, isDefault, onError] = useImageUrl({url, urls, idName, name, defaultToInitialLetter}); + const [imageUrl, onError] = useImageUrl({url, urls, idName, name, defaultToInitialLetter}); - if (isDefault) { + if (!imageUrl && defaultToInitialLetter) { const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = ( { const imgNode = ( Date: Sun, 24 May 2020 14:25:31 +0100 Subject: [PATCH 291/399] fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/components/views/messages/TextualBody-test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 212afac5c4..07cd51edbd 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -205,9 +205,8 @@ describe("", () => { expect(content.html()).toBe('' + 'Hey ' + '' + - 'Member' + + 'Member' + ''); }); }); From 50b8445d4dc231fc6c815b4cebc58618bb305ded Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 15:13:53 +0100 Subject: [PATCH 292/399] fix Content Messages upload abort Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/ContentMessages.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 4f5a1a1220..931cc56a34 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -258,10 +258,10 @@ function readFileAsArrayBuffer(file) { * If the file is encrypted then the object will have a "file" key. */ function uploadFile(matrixClient, roomId, file, progressHandler) { + let canceled = false; if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. // First read the file into memory. - let canceled = false; let uploadPromise; let encryptInfo; const prom = readFileAsArrayBuffer(file).then(function(data) { @@ -278,9 +278,9 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { progressHandler: progressHandler, includeFilename: false, }); - return uploadPromise; }).then(function(url) { + if (canceled) throw new UploadCanceledError(); // If the attachment is encrypted then bundle the URL along // with the information needed to decrypt the attachment and // add it under a file key. @@ -300,11 +300,14 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { progressHandler: progressHandler, }); const promise1 = basePromise.then(function(url) { + if (canceled) throw new UploadCanceledError(); // If the attachment isn't encrypted then include the URL directly. return {"url": url}; }); - // XXX: copy over the abort method to the new promise - promise1.abort = basePromise.abort; + promise1.abort = () => { + canceled = true; + MatrixClientPeg.get().cancelUpload(basePromise); + }; return promise1; } } @@ -489,11 +492,16 @@ export default class ContentMessages { } }); + prom.abort = () => { + upload.cancelled = true; + }; + const upload = { fileName: file.name || 'Attachment', roomId: roomId, total: 0, loaded: 0, + promise: prom, }; this.inprogress.push(upload); dis.dispatch({action: 'upload_started'}); @@ -510,6 +518,7 @@ export default class ContentMessages { } return prom.then(function() { + if (upload.canceled) throw new UploadCanceledError(); // XXX: upload.promise must be the promise that // is returned by uploadFile as it has an abort() // method hacked onto it. @@ -524,6 +533,7 @@ export default class ContentMessages { // Await previous message being sent into the room return promBefore; }).then(function() { + if (upload.canceled) throw new UploadCanceledError(); return matrixClient.sendMessage(roomId, content); }, function(err) { error = err; From 0705883c8fa958e011558b30111e54973b00bbe7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 15:47:52 +0100 Subject: [PATCH 293/399] Convert ContentMessages to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 3 + ...ContentMessages.js => ContentMessages.tsx} | 223 ++++++++++-------- 2 files changed, 127 insertions(+), 99 deletions(-) rename src/{ContentMessages.js => ContentMessages.tsx} (77%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index e6e339d067..b0e3159901 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -15,6 +15,7 @@ limitations under the License. */ import * as ModernizrStatic from "modernizr"; +import ContentMessages from "../ContentMessages"; declare global { interface Window { @@ -22,6 +23,8 @@ declare global { Olm: { init: () => Promise; }; + + mx_ContentMessages: ContentMessages; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/ContentMessages.js b/src/ContentMessages.tsx similarity index 77% rename from src/ContentMessages.js rename to src/ContentMessages.tsx index 931cc56a34..6de318f81d 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.tsx @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2019 New Vector Ltd +Copyright 2020 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. @@ -15,11 +16,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - +import React from "react"; import extend from './extend'; import dis from './dispatcher/dispatcher'; import {MatrixClientPeg} from './MatrixClientPeg'; +import {MatrixClient} from "matrix-js-sdk/src/client"; import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; @@ -39,6 +40,50 @@ const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01]; export class UploadCanceledError extends Error {} +type ThumbnailableElement = HTMLImageElement | HTMLVideoElement; + +interface IUpload { + fileName: string; + roomId: string; + total: number; + loaded: number; + promise: Promise; + canceled?: boolean; +} + +interface IMediaConfig { + "m.upload.size"?: number; +} + +interface IContent { + body: string; + msgtype: string; + info: { + size: number; + mimetype?: string; + }; + file?: string; + url?: string; +} + +interface IThumbnail { + info: { + thumbnail_info: { + w: number; + h: number; + mimetype: string; + size: number; + }; + w: number; + h: number; + }; + thumbnail: Blob; +} + +interface IAbortablePromise extends Promise { + abort(): void; +} + /** * Create a thumbnail for a image DOM element. * The image will be smaller than MAX_WIDTH and MAX_HEIGHT. @@ -51,13 +96,13 @@ export class UploadCanceledError extends Error {} * about the original image and the thumbnail. * * @param {HTMLElement} element The element to thumbnail. - * @param {integer} inputWidth The width of the image in the input element. - * @param {integer} inputHeight the width of the image in the input element. + * @param {number} inputWidth The width of the image in the input element. + * @param {number} inputHeight the width of the image in the input element. * @param {String} mimeType The mimeType to save the blob as. * @return {Promise} A promise that resolves with an object with an info key * and a thumbnail key. */ -function createThumbnail(element, inputWidth, inputHeight, mimeType) { +function createThumbnail(element: ThumbnailableElement, inputWidth: number, inputHeight: number, mimeType: string): Promise { return new Promise((resolve) => { let targetWidth = inputWidth; let targetHeight = inputHeight; @@ -98,7 +143,7 @@ function createThumbnail(element, inputWidth, inputHeight, mimeType) { * @param {File} imageFile The file to load in an image element. * @return {Promise} A promise that resolves with the html image element. */ -async function loadImageElement(imageFile) { +async function loadImageElement(imageFile: File) { // Load the file into an html element const img = document.createElement("img"); const objectUrl = URL.createObjectURL(imageFile); @@ -128,8 +173,7 @@ async function loadImageElement(imageFile) { for (const chunk of chunks) { if (chunk.name === 'pHYs') { if (chunk.data.byteLength !== PHYS_HIDPI.length) return; - const hidpi = chunk.data.every((val, i) => val === PHYS_HIDPI[i]); - return hidpi; + return chunk.data.every((val, i) => val === PHYS_HIDPI[i]); } } return false; @@ -152,7 +196,7 @@ async function loadImageElement(imageFile) { */ function infoForImageFile(matrixClient, roomId, imageFile) { let thumbnailType = "image/png"; - if (imageFile.type == "image/jpeg") { + if (imageFile.type === "image/jpeg") { thumbnailType = "image/jpeg"; } @@ -175,15 +219,15 @@ function infoForImageFile(matrixClient, roomId, imageFile) { * @param {File} videoFile The file to load in an video element. * @return {Promise} A promise that resolves with the video image element. */ -function loadVideoElement(videoFile) { +function loadVideoElement(videoFile): Promise { return new Promise((resolve, reject) => { // Load the file into an html element const video = document.createElement("video"); const reader = new FileReader(); - reader.onload = function(e) { - video.src = e.target.result; + reader.onload = function(ev) { + video.src = ev.target.result as string; // Once ready, returns its size // Wait until we have enough data to thumbnail the first frame. @@ -231,11 +275,11 @@ function infoForVideoFile(matrixClient, roomId, videoFile) { * @return {Promise} A promise that resolves with an ArrayBuffer when the file * is read. */ -function readFileAsArrayBuffer(file) { +function readFileAsArrayBuffer(file: File | Blob): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function(e) { - resolve(e.target.result); + resolve(e.target.result as ArrayBuffer); }; reader.onerror = function(e) { reject(e); @@ -257,7 +301,7 @@ function readFileAsArrayBuffer(file) { * If the file is unencrypted then the object will have a "url" key. * If the file is encrypted then the object will have a "file" key. */ -function uploadFile(matrixClient, roomId, file, progressHandler) { +function uploadFile(matrixClient: MatrixClient, roomId: string, file: File | Blob, progressHandler?: any) { let canceled = false; if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. @@ -290,7 +334,7 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { } return {"file": encryptInfo}; }); - prom.abort = () => { + (prom as IAbortablePromise).abort = () => { canceled = true; if (uploadPromise) MatrixClientPeg.get().cancelUpload(uploadPromise); }; @@ -313,30 +357,27 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { } export default class ContentMessages { - constructor() { - this.inprogress = []; - this.nextId = 0; - this._mediaConfig = null; - } + private inprogress: IUpload[] = []; + private mediaConfig: IMediaConfig = null; static sharedInstance() { - if (global.mx_ContentMessages === undefined) { - global.mx_ContentMessages = new ContentMessages(); + if (window.mx_ContentMessages === undefined) { + window.mx_ContentMessages = new ContentMessages(); } - return global.mx_ContentMessages; + return window.mx_ContentMessages; } - _isFileSizeAcceptable(file) { - if (this._mediaConfig !== null && - this._mediaConfig["m.upload.size"] !== undefined && - file.size > this._mediaConfig["m.upload.size"]) { + _isFileSizeAcceptable(file: File) { + if (this.mediaConfig !== null && + this.mediaConfig["m.upload.size"] !== undefined && + file.size > this.mediaConfig["m.upload.size"]) { return false; } return true; } _ensureMediaConfigFetched() { - if (this._mediaConfig !== null) return; + if (this.mediaConfig !== null) return; console.log("[Media Config] Fetching"); return MatrixClientPeg.get().getMediaConfig().then((config) => { @@ -347,11 +388,11 @@ export default class ContentMessages { console.log("[Media Config] Could not fetch config, so not limiting uploads."); return {}; }).then((config) => { - this._mediaConfig = config; + this.mediaConfig = config; }); } - sendStickerContentToRoom(url, roomId, info, text, matrixClient) { + sendStickerContentToRoom(url: string, roomId: string, info: string, text: string, matrixClient: MatrixClient) { return MatrixClientPeg.get().sendStickerMessage(roomId, url, info, text).catch((e) => { console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e); throw e; @@ -359,14 +400,14 @@ export default class ContentMessages { } getUploadLimit() { - if (this._mediaConfig !== null && this._mediaConfig["m.upload.size"] !== undefined) { - return this._mediaConfig["m.upload.size"]; + if (this.mediaConfig !== null && this.mediaConfig["m.upload.size"] !== undefined) { + return this.mediaConfig["m.upload.size"]; } else { return null; } } - async sendContentListToRoom(files, roomId, matrixClient) { + async sendContentListToRoom(files: File[], roomId: string, matrixClient: MatrixClient) { if (matrixClient.isGuest()) { dis.dispatch({action: 'require_registration'}); return; @@ -375,22 +416,18 @@ export default class ContentMessages { const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); if (isQuoting) { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - const shouldUpload = await new Promise((resolve) => { - Modal.createTrackedDialog('Upload Reply Warning', '', QuestionDialog, { - title: _t('Replying With Files'), - description: ( -
    {_t( - 'At this time it is not possible to reply with a file. ' + - 'Would you like to upload this file without replying?', - )}
    - ), - hasCancelButton: true, - button: _t("Continue"), - onFinished: (shouldUpload) => { - resolve(shouldUpload); - }, - }); + const {finished} = Modal.createTrackedDialog('Upload Reply Warning', '', QuestionDialog, { + title: _t('Replying With Files'), + description: ( +
    {_t( + 'At this time it is not possible to reply with a file. ' + + 'Would you like to upload this file without replying?', + )}
    + ), + hasCancelButton: true, + button: _t("Continue"), }); + const [shouldUpload]: [boolean] = await finished; if (!shouldUpload) return; } @@ -409,17 +446,12 @@ export default class ContentMessages { if (tooBigFiles.length > 0) { const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); - const uploadFailureDialogPromise = new Promise((resolve) => { - Modal.createTrackedDialog('Upload Failure', '', UploadFailureDialog, { - badFiles: tooBigFiles, - totalFiles: files.length, - contentMessages: this, - onFinished: (shouldContinue) => { - resolve(shouldContinue); - }, - }); + const {finished} = Modal.createTrackedDialog('Upload Failure', '', UploadFailureDialog, { + badFiles: tooBigFiles, + totalFiles: files.length, + contentMessages: this, }); - const shouldContinue = await uploadFailureDialogPromise; + const [shouldContinue]: [boolean] = await finished; if (!shouldContinue) return; } @@ -431,31 +463,28 @@ export default class ContentMessages { for (let i = 0; i < okFiles.length; ++i) { const file = okFiles[i]; if (!uploadAll) { - const shouldContinue = await new Promise((resolve) => { - Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { - file, - currentIndex: i, - totalFiles: okFiles.length, - onFinished: (shouldContinue, shouldUploadAll) => { - if (shouldUploadAll) { - uploadAll = true; - } - resolve(shouldContinue); - }, - }); + const {finished} = Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + file, + currentIndex: i, + totalFiles: okFiles.length, }); + const [shouldContinue, shouldUploadAll]: [boolean, boolean] = await finished; if (!shouldContinue) break; + if (shouldUploadAll) { + uploadAll = true; + } } promBefore = this._sendContentToRoom(file, roomId, matrixClient, promBefore); } } - _sendContentToRoom(file, roomId, matrixClient, promBefore) { - const content = { + _sendContentToRoom(file: File, roomId: string, matrixClient: MatrixClient, promBefore: Promise) { + const content: IContent = { body: file.name || 'Attachment', info: { size: file.size, }, + msgtype: "", // set later }; // if we have a mime type for the file, add it to the message metadata @@ -464,25 +493,25 @@ export default class ContentMessages { } const prom = new Promise((resolve) => { - if (file.type.indexOf('image/') == 0) { + if (file.type.indexOf('image/') === 0) { content.msgtype = 'm.image'; - infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{ + infoForImageFile(matrixClient, roomId, file).then((imageInfo) => { extend(content.info, imageInfo); resolve(); - }, (error)=>{ - console.error(error); + }, (e) => { + console.error(e); content.msgtype = 'm.file'; resolve(); }); - } else if (file.type.indexOf('audio/') == 0) { + } else if (file.type.indexOf('audio/') === 0) { content.msgtype = 'm.audio'; resolve(); - } else if (file.type.indexOf('video/') == 0) { + } else if (file.type.indexOf('video/') === 0) { content.msgtype = 'm.video'; - infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{ + infoForVideoFile(matrixClient, roomId, file).then((videoInfo) => { extend(content.info, videoInfo); resolve(); - }, (error)=>{ + }, (e) => { content.msgtype = 'm.file'; resolve(); }); @@ -492,14 +521,15 @@ export default class ContentMessages { } }); - prom.abort = () => { - upload.cancelled = true; + // create temporary abort handler for before the actual upload gets passed off to js-sdk + (prom as IAbortablePromise).abort = () => { + upload.canceled = true; }; - const upload = { + const upload: IUpload = { fileName: file.name || 'Attachment', roomId: roomId, - total: 0, + total: file.size, loaded: 0, promise: prom, }; @@ -509,14 +539,13 @@ export default class ContentMessages { // Focus the composer view dis.dispatch({action: 'focus_composer'}); - let error; - function onProgress(ev) { upload.total = ev.total; upload.loaded = ev.loaded; dis.dispatch({action: 'upload_progress', upload: upload}); } + let error; return prom.then(function() { if (upload.canceled) throw new UploadCanceledError(); // XXX: upload.promise must be the promise that @@ -529,7 +558,7 @@ export default class ContentMessages { content.file = result.file; content.url = result.url; }); - }).then((url) => { + }).then(() => { // Await previous message being sent into the room return promBefore; }).then(function() { @@ -539,7 +568,7 @@ export default class ContentMessages { error = err; if (!upload.canceled) { let desc = _t("The file '%(fileName)s' failed to upload.", {fileName: upload.fileName}); - if (err.http_status == 413) { + if (err.http_status === 413) { desc = _t( "The file '%(fileName)s' exceeds this homeserver's size limit for uploads", {fileName: upload.fileName}, @@ -552,11 +581,9 @@ export default class ContentMessages { }); } }).finally(() => { - const inprogressKeys = Object.keys(this.inprogress); for (let i = 0; i < this.inprogress.length; ++i) { - const k = inprogressKeys[i]; - if (this.inprogress[k].promise === upload.promise) { - this.inprogress.splice(k, 1); + if (this.inprogress[i].promise === upload.promise) { + this.inprogress.splice(i, 1); break; } } @@ -565,7 +592,7 @@ export default class ContentMessages { // clear the media size limit so we fetch it again next time // we try to upload if (error && error.http_status === 413) { - this._mediaConfig = null; + this.mediaConfig = null; } dis.dispatch({action: 'upload_failed', upload, error}); } else { @@ -579,13 +606,11 @@ export default class ContentMessages { return this.inprogress.filter(u => !u.canceled); } - cancelUpload(promise) { - const inprogressKeys = Object.keys(this.inprogress); + cancelUpload(promise: Promise) { let upload; for (let i = 0; i < this.inprogress.length; ++i) { - const k = inprogressKeys[i]; - if (this.inprogress[k].promise === promise) { - upload = this.inprogress[k]; + if (this.inprogress[i].promise === promise) { + upload = this.inprogress[i]; break; } } From 4a08c8cedaedd2809f0d5eae1a5719cf2e659a0f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 16:00:57 +0100 Subject: [PATCH 294/399] sort file Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/ContentMessages.tsx | 100 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 6de318f81d..bf9435914c 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -360,38 +360,6 @@ export default class ContentMessages { private inprogress: IUpload[] = []; private mediaConfig: IMediaConfig = null; - static sharedInstance() { - if (window.mx_ContentMessages === undefined) { - window.mx_ContentMessages = new ContentMessages(); - } - return window.mx_ContentMessages; - } - - _isFileSizeAcceptable(file: File) { - if (this.mediaConfig !== null && - this.mediaConfig["m.upload.size"] !== undefined && - file.size > this.mediaConfig["m.upload.size"]) { - return false; - } - return true; - } - - _ensureMediaConfigFetched() { - if (this.mediaConfig !== null) return; - - console.log("[Media Config] Fetching"); - return MatrixClientPeg.get().getMediaConfig().then((config) => { - console.log("[Media Config] Fetched config:", config); - return config; - }).catch(() => { - // Media repo can't or won't report limits, so provide an empty object (no limits). - console.log("[Media Config] Could not fetch config, so not limiting uploads."); - return {}; - }).then((config) => { - this.mediaConfig = config; - }); - } - sendStickerContentToRoom(url: string, roomId: string, info: string, text: string, matrixClient: MatrixClient) { return MatrixClientPeg.get().sendStickerMessage(roomId, url, info, text).catch((e) => { console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e); @@ -431,13 +399,13 @@ export default class ContentMessages { if (!shouldUpload) return; } - await this._ensureMediaConfigFetched(); + await this.ensureMediaConfigFetched(); const tooBigFiles = []; const okFiles = []; for (let i = 0; i < files.length; ++i) { - if (this._isFileSizeAcceptable(files[i])) { + if (this.isFileSizeAcceptable(files[i])) { okFiles.push(files[i]); } else { tooBigFiles.push(files[i]); @@ -474,11 +442,30 @@ export default class ContentMessages { uploadAll = true; } } - promBefore = this._sendContentToRoom(file, roomId, matrixClient, promBefore); + promBefore = this.sendContentToRoom(file, roomId, matrixClient, promBefore); } } - _sendContentToRoom(file: File, roomId: string, matrixClient: MatrixClient, promBefore: Promise) { + getCurrentUploads() { + return this.inprogress.filter(u => !u.canceled); + } + + cancelUpload(promise: Promise) { + let upload: IUpload; + for (let i = 0; i < this.inprogress.length; ++i) { + if (this.inprogress[i].promise === promise) { + upload = this.inprogress[i]; + break; + } + } + if (upload) { + upload.canceled = true; + MatrixClientPeg.get().cancelUpload(upload.promise); + dis.dispatch({action: 'upload_canceled', upload}); + } + } + + private sendContentToRoom(file: File, roomId: string, matrixClient: MatrixClient, promBefore: Promise) { const content: IContent = { body: file.name || 'Attachment', info: { @@ -602,22 +589,35 @@ export default class ContentMessages { }); } - getCurrentUploads() { - return this.inprogress.filter(u => !u.canceled); + private isFileSizeAcceptable(file: File) { + if (this.mediaConfig !== null && + this.mediaConfig["m.upload.size"] !== undefined && + file.size > this.mediaConfig["m.upload.size"]) { + return false; + } + return true; } - cancelUpload(promise: Promise) { - let upload; - for (let i = 0; i < this.inprogress.length; ++i) { - if (this.inprogress[i].promise === promise) { - upload = this.inprogress[i]; - break; - } - } - if (upload) { - upload.canceled = true; - MatrixClientPeg.get().cancelUpload(upload.promise); - dis.dispatch({action: 'upload_canceled', upload}); + private ensureMediaConfigFetched() { + if (this.mediaConfig !== null) return; + + console.log("[Media Config] Fetching"); + return MatrixClientPeg.get().getMediaConfig().then((config) => { + console.log("[Media Config] Fetched config:", config); + return config; + }).catch(() => { + // Media repo can't or won't report limits, so provide an empty object (no limits). + console.log("[Media Config] Could not fetch config, so not limiting uploads."); + return {}; + }).then((config) => { + this.mediaConfig = config; + }); + } + + static sharedInstance() { + if (window.mx_ContentMessages === undefined) { + window.mx_ContentMessages = new ContentMessages(); } + return window.mx_ContentMessages; } } From db6853e022307bf367fe407f845e6f3d8dbdc01b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 24 May 2020 20:00:02 +0100 Subject: [PATCH 295/399] Fix Emoji Picker footer being too small if text overflows Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/emojipicker/_EmojiPicker.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/emojipicker/_EmojiPicker.scss b/res/css/views/emojipicker/_EmojiPicker.scss index 24561eeeb9..400e40e233 100644 --- a/res/css/views/emojipicker/_EmojiPicker.scss +++ b/res/css/views/emojipicker/_EmojiPicker.scss @@ -190,7 +190,7 @@ limitations under the License. .mx_EmojiPicker_footer { border-top: 1px solid $message-action-bar-border-color; - height: 72px; + min-height: 72px; display: flex; align-items: center; From 1f52b5e2036ef0c6fb1192f9db9df41a63f562bd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 25 May 2020 10:59:31 +0100 Subject: [PATCH 296/399] Update Crypto Store Too New copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/MatrixClientPeg.js | 4 +--- src/components/views/dialogs/CryptoStoreTooNewDialog.js | 6 ++---- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 21f05b9759..704f1052fc 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -158,9 +158,7 @@ class _MatrixClientPeg { // The js-sdk found a crypto DB too new for it to use const CryptoStoreTooNewDialog = sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog"); - Modal.createDialog(CryptoStoreTooNewDialog, { - host: window.location.host, - }); + Modal.createDialog(CryptoStoreTooNewDialog); } // this can happen for a number of reasons, the most likely being // that the olm library was missing. It's not fatal. diff --git a/src/components/views/dialogs/CryptoStoreTooNewDialog.js b/src/components/views/dialogs/CryptoStoreTooNewDialog.js index 081e84696c..4694619601 100644 --- a/src/components/views/dialogs/CryptoStoreTooNewDialog.js +++ b/src/components/views/dialogs/CryptoStoreTooNewDialog.js @@ -42,11 +42,9 @@ export default (props) => { }; const description = - _t("You've previously used a newer version of Riot on %(host)s. " + + _t("You've previously used a newer version of Riot with this session. " + "To use this version again with end to end encryption, you will " + - "need to sign out and back in again. ", - {host: props.host}, - ); + "need to sign out and back in again."); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 96ccf1589d..67ee31e71e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1597,7 +1597,7 @@ "Create Room": "Create Room", "Sign out": "Sign out", "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": "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", - "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. ": "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. ", + "You've previously used a newer version of Riot with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "You've previously used a newer version of Riot with this session. To use this version again with end to end encryption, you will need to sign out and back in again.", "Incompatible Database": "Incompatible Database", "Continue With Encryption Disabled": "Continue With Encryption Disabled", "Confirm your account deactivation by using Single Sign On to prove your identity.": "Confirm your account deactivation by using Single Sign On to prove your identity.", From c372e8edee78520346e778f69769bbd84b6660c5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 25 May 2020 11:29:06 +0100 Subject: [PATCH 297/399] fix viewGroup to actually show the group if possible Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 48dc72f4fa..b1076c6ec9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -932,9 +932,20 @@ export default class MatrixChat extends React.PureComponent { }); } - private viewGroup(payload) { + private async viewGroup(payload) { const groupId = payload.group_id; + + // Wait for the first sync to complete + if (!this.firstSyncComplete) { + if (!this.firstSyncPromise) { + console.warn('Cannot view a group before first sync. group_id:', groupId); + return; + } + await this.firstSyncPromise.promise; + } + this.setState({ + view: Views.LOGGED_IN, currentGroupId: groupId, currentGroupIsNew: payload.group_is_new, }); From c73c3ae9271365ce94e48ae4a74436174b8697f8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 25 May 2020 11:35:21 +0100 Subject: [PATCH 298/399] Update confirm passphrase copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/dialogs/keybackup/CreateKeyBackupDialog.js | 5 ++++- .../views/dialogs/secretstorage/CreateSecretStorageDialog.js | 5 ++++- src/i18n/strings/en_EN.json | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 532b2f960f..7c5170fab6 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -284,8 +284,10 @@ export default class CreateKeyBackupDialog extends React.PureComponent { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let matchText; + let changeText; if (this.state.passPhraseConfirm === this.state.passPhrase) { matchText = _t("That matches!"); + changeText = _t("Use a different passphrase?"); } else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) { // only tell them they're wrong if they've actually gone wrong. // Security concious readers will note that if you left riot-web unattended @@ -295,6 +297,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { // Note that not having typed anything at all will not hit this clause and // fall through so empty box === no hint. matchText = _t("That doesn't match."); + changeText = _t("Go back to set it again."); } let passPhraseMatch = null; @@ -303,7 +306,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
    {matchText}
    - {_t("Go back to set it again.")} + {changeText}
    ; diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 12b71206d0..e6ab07c449 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -538,8 +538,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent { const Field = sdk.getComponent('views.elements.Field'); let matchText; + let changeText; if (this.state.passPhraseConfirm === this.state.passPhrase) { matchText = _t("That matches!"); + changeText = _t("Use a different passphrase?"); } else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) { // only tell them they're wrong if they've actually gone wrong. // Security concious readers will note that if you left riot-web unattended @@ -549,6 +551,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { // Note that not having typed anything at all will not hit this clause and // fall through so empty box === no hint. matchText = _t("That doesn't match."); + changeText = _t("Go back to set it again."); } let passPhraseMatch = null; @@ -557,7 +560,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
    {matchText}
    - {_t("Go back to set it again.")} + {changeText}
    ; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 96ccf1589d..ded320250b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2217,6 +2217,7 @@ "Back up encrypted message keys": "Back up encrypted message keys", "Set up with a recovery key": "Set up with a recovery key", "That matches!": "That matches!", + "Use a different passphrase?": "Use a different passphrase?", "That doesn't match.": "That doesn't match.", "Go back to set it again.": "Go back to set it again.", "Enter your recovery passphrase a second time to confirm it.": "Enter your recovery passphrase a second time to confirm it.", From 63f78b0808f89dc0fbffd70cc3d176d45d1a57b3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 25 May 2020 13:40:05 +0100 Subject: [PATCH 299/399] Move tooltip to ts --- package.json | 2 + .../views/elements/{Field.js => Field.tsx} | 159 ++++++++++-------- .../elements/{Tooltip.js => Tooltip.tsx} | 86 +++++----- .../views/settings/account/PhoneNumbers.js | 2 +- src/dispatcher/actions.ts | 5 + src/dispatcher/payloads/ViewUserPayload.ts | 17 ++ yarn.lock | 12 ++ 7 files changed, 170 insertions(+), 113 deletions(-) rename src/components/views/elements/{Field.js => Field.tsx} (67%) rename src/components/views/elements/{Tooltip.js => Tooltip.tsx} (71%) diff --git a/package.json b/package.json index 7c008d5ccc..620957dd04 100644 --- a/package.json +++ b/package.json @@ -118,9 +118,11 @@ "@peculiar/webcrypto": "^1.0.22", "@types/classnames": "^2.2.10", "@types/flux": "^3.1.9", + "@types/lodash": "^4.14.152", "@types/modernizr": "^3.5.3", "@types/qrcode": "^1.3.4", "@types/react": "16.9", + "@types/react-dom": "^16.9.8", "@types/zxcvbn": "^4.4.0", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.tsx similarity index 67% rename from src/components/views/elements/Field.js rename to src/components/views/elements/Field.tsx index 2ebb90da26..100a6ebf56 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.tsx @@ -15,10 +15,9 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import * as sdk from '../../../index'; -import { debounce } from 'lodash'; +import { debounce, Cancelable } from 'lodash'; // Invoke validation from user input (when typing, etc.) at most once every N ms. const VALIDATION_THROTTLE_MS = 200; @@ -29,51 +28,88 @@ function getId() { return `${BASE_ID}_${count++}`; } -export default class Field extends React.PureComponent { - static propTypes = { - // The field's ID, which binds the input and label together. Immutable. - id: PropTypes.string, - // The element to create. Defaults to "input". - // To define options for a select, use - element: PropTypes.oneOf(["input", "select", "textarea"]), - // The field's type (when used as an ). Defaults to "text". - type: PropTypes.string, - // id of a element for suggestions - list: PropTypes.string, - // The field's label string. - label: PropTypes.string, - // The field's placeholder string. Defaults to the label. - placeholder: PropTypes.string, - // The field's value. - // This is a controlled component, so the value is required. - value: PropTypes.string.isRequired, - // Optional component to include inside the field before the input. - prefix: PropTypes.node, - // Optional component to include inside the field after the input. - postfix: PropTypes.node, - // The callback called whenever the contents of the field - // changes. Returns an object with `valid` boolean field - // and a `feedback` react component field to provide feedback - // to the user. - onValidate: PropTypes.func, - // If specified, overrides the value returned by onValidate. - flagInvalid: PropTypes.bool, - // If specified, contents will appear as a tooltip on the element and - // validation feedback tooltips will be suppressed. - tooltipContent: PropTypes.node, - // If specified alongside tooltipContent, the class name to apply to the - // tooltip itself. - tooltipClassName: PropTypes.string, - // If specified, an additional class name to apply to the field container - className: PropTypes.string, - // All other props pass through to the . - }; +interface IProps extends React.HTMLAttributes { + // The field's ID, which binds the input and label together. Immutable. + id?: string, + // The element to create. Defaults to "input". + // To define options for a select, use + element?: InputType, + // The field's type (when used as an ). Defaults to "text". + type?: string, + // id of a element for suggestions + list?: string, + // The field's label string. + label?: string, + // The field's placeholder string. Defaults to the label. + placeholder?: string, + // The field's value. + // This is a controlled component, so the value is required. + value: string, + // Optional component to include inside the field before the input. + prefixComponent?: React.ReactNode, + // Optional component to include inside the field after the input. + postfixComponent?: React.ReactNode, + // The callback called whenever the contents of the field + // changes. Returns an object with `valid` boolean field + // and a `feedback` react component field to provide feedback + // to the user. + onValidate?: ( + args: {value: string, focused: boolean, allowEmpty: boolean} + ) => {valid: boolean, feedback: React.ReactNode}, + // If specified, overrides the value returned by onValidate. + flagInvalid?: boolean, + // If specified, contents will appear as a tooltip on the element and + // validation feedback tooltips will be suppressed. + tooltipContent?: React.ReactNode, + // If specified alongside tooltipContent, the class name to apply to the + // tooltip itself. + tooltipClassName?: string, + // If specified, an additional class name to apply to the field container + className?: string, + // All other props pass through to the . +} +enum InputType { + INPUT = "input", + SELECT = "select", + TEXTAREA = "textarea", +} + +interface IState { + valid: boolean, + feedback: React.ReactNode, + feedbackVisible: boolean, + focused: boolean, +} + +export default class Field extends React.PureComponent { + private id: string; + private input: HTMLInputElement; + + /* + * This was changed from throttle to debounce: this is more traditional for + * form validation since it means that the validation doesn't happen at all + * until the user stops typing for a bit (debounce defaults to not running on + * the leading edge). If we're doing an HTTP hit on each validation, we have more + * incentive to prevent validating input that's very unlikely to be valid. + * We may find that we actually want different behaviour for registration + * fields, in which case we can add some options to control it. + */ + validateOnChange = debounce(() => { + this.validate({ + focused: true, + }); + }, VALIDATION_THROTTLE_MS); + + focus() { + this.input.focus(); + } constructor(props) { super(props); this.state = { valid: undefined, feedback: undefined, + feedbackVisible: false, focused: false, }; @@ -114,11 +150,7 @@ export default class Field extends React.PureComponent { } }; - focus() { - this.input.focus(); - } - - async validate({ focused, allowEmpty = true }) { + async validate({ focused, allowEmpty = true }: {focused: boolean, allowEmpty?: boolean}) { if (!this.props.onValidate) { return; } @@ -149,48 +181,37 @@ export default class Field extends React.PureComponent { } } - /* - * This was changed from throttle to debounce: this is more traditional for - * form validation since it means that the validation doesn't happen at all - * until the user stops typing for a bit (debounce defaults to not running on - * the leading edge). If we're doing an HTTP hit on each validation, we have more - * incentive to prevent validating input that's very unlikely to be valid. - * We may find that we actually want different behaviour for registration - * fields, in which case we can add some options to control it. - */ - validateOnChange = debounce(() => { - this.validate({ - focused: true, - }); - }, VALIDATION_THROTTLE_MS); + render() { const { - element, prefix, postfix, className, onValidate, children, + element, prefixComponent, postfixComponent, className, onValidate, children, tooltipContent, flagInvalid, tooltipClassName, list, ...inputProps} = this.props; const inputElement = element || "input"; // Set some defaults for the element inputProps.type = inputProps.type || "text"; - inputProps.ref = input => this.input = input; + const ref = input => this.input = input; inputProps.placeholder = inputProps.placeholder || inputProps.label; inputProps.id = this.id; // this overwrites the id from props inputProps.onFocus = this.onFocus; inputProps.onChange = this.onChange; inputProps.onBlur = this.onBlur; - inputProps.list = list; - const fieldInput = React.createElement(inputElement, inputProps, children); + // Appease typescript's inference + const inputProps_ = {...inputProps, ref, list}; + + const fieldInput = React.createElement(inputElement, inputProps_, children); let prefixContainer = null; - if (prefix) { - prefixContainer = {prefix}; + if (prefixComponent) { + prefixContainer = {prefixComponent}; } let postfixContainer = null; - if (postfix) { - postfixContainer = {postfix}; + if (postfixComponent) { + postfixContainer = {postfixComponent}; } const hasValidationFlag = flagInvalid !== null && flagInvalid !== undefined; @@ -198,7 +219,7 @@ export default class Field extends React.PureComponent { // If we have a prefix element, leave the label always at the top left and // don't animate it, as it looks a bit clunky and would add complexity to do // properly. - mx_Field_labelAlwaysTopLeft: prefix, + mx_Field_labelAlwaysTopLeft: prefixComponent, mx_Field_valid: onValidate && this.state.valid === true, mx_Field_invalid: hasValidationFlag ? flagInvalid diff --git a/src/components/views/elements/Tooltip.js b/src/components/views/elements/Tooltip.tsx similarity index 71% rename from src/components/views/elements/Tooltip.js rename to src/components/views/elements/Tooltip.tsx index 4807ade3db..753052717c 100644 --- a/src/components/views/elements/Tooltip.js +++ b/src/components/views/elements/Tooltip.tsx @@ -18,67 +18,68 @@ limitations under the License. */ -import React from 'react'; +import React, { Component } from 'react'; import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import createReactClass from 'create-react-class'; import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; +import { ViewTooltipPayload } from '../../../dispatcher/payloads/ViewUserPayload'; +import { Action } from '../../../dispatcher/actions'; const MIN_TOOLTIP_HEIGHT = 25; -export default createReactClass({ - displayName: 'Tooltip', - - propTypes: { +interface IProps { // Class applied to the element used to position the tooltip - className: PropTypes.string, + className: string, // Class applied to the tooltip itself - tooltipClassName: PropTypes.string, + tooltipClassName: string, // Whether the tooltip is visible or hidden. // The hidden state allows animating the tooltip away via CSS. // Defaults to visible if unset. - visible: PropTypes.bool, + visible: boolean, // the react element to put into the tooltip - label: PropTypes.node, - }, + label: React.ReactNode, +} - getDefaultProps() { - return { - visible: true, - }; - }, +class Tooltip extends React.Component { + private tooltipContainer: HTMLElement; + private tooltip: void | Element | Component; + private parent: Element; + + + static defaultProps = { + visible: true, + }; // Create a wrapper for the tooltip outside the parent and attach it to the body element - componentDidMount: function() { + componentDidMount() { this.tooltipContainer = document.createElement("div"); this.tooltipContainer.className = "mx_Tooltip_wrapper"; document.body.appendChild(this.tooltipContainer); - window.addEventListener('scroll', this._renderTooltip, true); + window.addEventListener('scroll', this.renderTooltip, true); - this.parent = ReactDOM.findDOMNode(this).parentNode; + this.parent = ReactDOM.findDOMNode(this).parentNode as Element; - this._renderTooltip(); - }, + this.renderTooltip(); + } - componentDidUpdate: function() { - this._renderTooltip(); - }, + componentDidUpdate() { + this.renderTooltip(); + } // Remove the wrapper element, as the tooltip has finished using it - componentWillUnmount: function() { - dis.dispatch({ - action: 'view_tooltip', + componentWillUnmount() { + dis.dispatch({ + action: Action.ViewTooltip, tooltip: null, parent: null, }); ReactDOM.unmountComponentAtNode(this.tooltipContainer); document.body.removeChild(this.tooltipContainer); - window.removeEventListener('scroll', this._renderTooltip, true); - }, + window.removeEventListener('scroll', this.renderTooltip, true); + } - _updatePosition(style) { + private updatePosition(style: {[key: string]: any}) { const parentBox = this.parent.getBoundingClientRect(); let offset = 0; if (parentBox.height > MIN_TOOLTIP_HEIGHT) { @@ -91,16 +92,15 @@ export default createReactClass({ style.top = (parentBox.top - 2) + window.pageYOffset + offset; style.left = 6 + parentBox.right + window.pageXOffset; return style; - }, + } - _renderTooltip: function() { + private renderTooltip() { // Add the parent's position to the tooltips, so it's correctly // positioned, also taking into account any window zoom // NOTE: The additional 6 pixels for the left position, is to take account of the // tooltips chevron - const parent = ReactDOM.findDOMNode(this).parentNode; - let style = {}; - style = this._updatePosition(style); + const parent = ReactDOM.findDOMNode(this).parentNode as Element; + const style = this.updatePosition({}); // Hide the entire container when not visible. This prevents flashing of the tooltip // if it is not meant to be visible on first mount. style.display = this.props.visible ? "block" : "none"; @@ -118,21 +118,21 @@ export default createReactClass({ ); // Render the tooltip manually, as we wish it not to be rendered within the parent - this.tooltip = ReactDOM.render(tooltip, this.tooltipContainer); + this.tooltip = ReactDOM.render(tooltip, this.tooltipContainer); // Tell the roomlist about us so it can manipulate us if it wishes - dis.dispatch({ - action: 'view_tooltip', + dis.dispatch({ + action: Action.ViewTooltip, tooltip: this.tooltip, parent: parent, }); - }, + } - render: function() { + render() { // Render a placeholder return (
    ); - }, -}); + } +} diff --git a/src/components/views/settings/account/PhoneNumbers.js b/src/components/views/settings/account/PhoneNumbers.js index ad2dabd8ae..02e995ac45 100644 --- a/src/components/views/settings/account/PhoneNumbers.js +++ b/src/components/views/settings/account/PhoneNumbers.js @@ -267,7 +267,7 @@ export default class PhoneNumbers extends React.Component { label={_t("Phone Number")} autoComplete="off" disabled={this.state.verifying} - prefix={phoneCountry} + prefixComponent={phoneCountry} value={this.state.newPhoneNumber} onChange={this._onChangeNewPhoneNumber} /> diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index a2f9c3efe3..9cd9f7c9ba 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -38,5 +38,10 @@ export enum Action { * Open the user settings. No additional payload information required. */ ViewUserSettings = "view_user_settings", + + /** + * Sets the current tooltip + */ + ViewTooltip = "view_tooltip", } diff --git a/src/dispatcher/payloads/ViewUserPayload.ts b/src/dispatcher/payloads/ViewUserPayload.ts index ed602d4e24..d1f6db8968 100644 --- a/src/dispatcher/payloads/ViewUserPayload.ts +++ b/src/dispatcher/payloads/ViewUserPayload.ts @@ -17,6 +17,7 @@ limitations under the License. import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; +import { Component } from "react"; export interface ViewUserPayload extends ActionPayload { action: Action.ViewUser, @@ -27,3 +28,19 @@ export interface ViewUserPayload extends ActionPayload { */ member?: RoomMember; } + +export interface ViewTooltipPayload extends ActionPayload { + action: Action.ViewTooltip, + + /* + * The tooltip to render. If it's null the tooltip will not be rendered + * We need the void type because of typescript headaches. + */ + tooltip: null | void | Element | Component; + + /* + * The parent under which to render the tooltip. Can be null to remove + * the parent type. + */ + parent: null | Element +} diff --git a/yarn.lock b/yarn.lock index 93118dab22..9253442b7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1265,6 +1265,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/lodash@^4.14.152": + version "4.14.152" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.152.tgz#7e7679250adce14e749304cdb570969f77ec997c" + integrity sha512-Vwf9YF2x1GE3WNeUMjT5bTHa2DqgUo87ocdgTScupY2JclZ5Nn7W2RLM/N0+oreexUk8uaVugR81NnTY/jNNXg== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1292,6 +1297,13 @@ dependencies: "@types/node" "*" +"@types/react-dom@^16.9.8": + version "16.9.8" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" + integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== + dependencies: + "@types/react" "*" + "@types/react@*": version "16.9.35" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368" From 10e433314c77ecfeca55ea91e15a2920a49d8670 Mon Sep 17 00:00:00 2001 From: "J. A. Durieux" Date: Sat, 23 May 2020 21:31:51 +0000 Subject: [PATCH 300/399] Translated using Weblate (Dutch) Currently translated at 91.6% (2126 of 2322 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 | 105 +++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 5dfb779ff1..b4bb5dc5bd 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -45,7 +45,7 @@ "Continue": "Doorgaan", "Could not connect to the integration server": "Verbinding met de integratieserver is mislukt", "Cancel": "Annuleren", - "Accept": "Aanvaarden", + "Accept": "Aannemen", "Active call (%(roomName)s)": "Actieve oproep (%(roomName)s)", "Add": "Toevoegen", "Add a topic": "Voeg een onderwerp toe", @@ -140,7 +140,7 @@ "Create Room": "Gesprek aanmaken", "Curve25519 identity key": "Curve25519-identiteitssleutel", "/ddg is not a command": "/ddg is geen opdracht", - "Deactivate Account": "Account deactiveren", + "Deactivate Account": "Account sluiten", "Decline": "Weigeren", "Decrypt %(text)s": "%(text)s ontsleutelen", "Decryption error": "Ontsleutelingsfout", @@ -283,7 +283,7 @@ "The phone number entered looks invalid": "Het ingevoerde telefoonnummer ziet er ongeldig uit", "This email address is already in use": "Dit e-mailadres is al in gebruik", "This email address was not found": "Dit e-mailadres is niet gevonden", - "The email address linked to your account must be entered.": "Het e-mailadres dat met uw account verbonden is moet ingevoerd worden.", + "The email address linked to your account must be entered.": "Het aan uw account gekoppelde e-mailadres dient ingevoerd worden.", "The remote side failed to pick up": "De andere kant heeft niet opgenomen", "This room has no local addresses": "Dit gesprek heeft geen lokale adressen", "This room is not recognised.": "Dit gesprek wordt niet herkend.", @@ -378,7 +378,7 @@ "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Hiermee kunt u vanuit een andere Matrix-cliënt weggeschreven versleutelingssleutels inlezen, zodat u alle berichten die de andere cliënt kon ontcijferen ook hier kunt lezen.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Het weggeschreven bestand is beveiligd met een wachtwoord. Voer dat wachtwoord hier in om het bestand te ontsleutelen.", "You must join the room to see its files": "Slechts na toetreding tot het gesprek zult u de bestanden kunnen zien", - "Reject all %(invitedRooms)s invites": "Alle %(invitedRooms)s-uitnodigingen weigeren", + "Reject all %(invitedRooms)s invites": "Alle %(invitedRooms)s de uitnodigingen weigeren", "Failed to invite": "Uitnodigen is mislukt", "Failed to invite the following users to the %(roomName)s room:": "Kon de volgende gebruikers niet uitnodigen voor gesprek %(roomName)s:", "Confirm Removal": "Verwijdering bevestigen", @@ -584,11 +584,11 @@ "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s zijn weggegaan en weer toegetreden", "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s is %(count)s keer weggegaan en weer toegetreden", "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s is weggegaan en weer toegetreden", - "%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s hebben hun uitnodigingen %(count)s keer afgewezen", - "%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s hebben hun uitnodigingen afgewezen", + "%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s hebben hun uitnodigingen %(count)s maal afgeslagen", + "%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s hebben hun uitnodigingen afgeslagen", "%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s heeft de uitnodiging %(count)s maal geweigerd", "%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)s heeft de uitnodiging geweigerd", - "%(severalUsers)shad their invitations withdrawn %(count)s times|other": "De uitnodigingen van %(severalUsers)s zijn %(count)s keer ingetrokken", + "%(severalUsers)shad their invitations withdrawn %(count)s times|other": "%(severalUsers)s hebben hun uitnodigingen %(count)s maal ingetrokken", "%(severalUsers)shad their invitations withdrawn %(count)s times|one": "De uitnodigingen van %(severalUsers)s zijn ingetrokken", "%(oneUser)shad their invitation withdrawn %(count)s times|other": "De uitnodiging van %(oneUser)s is %(count)s keer ingetrokken", "%(oneUser)shad their invitation withdrawn %(count)s times|one": "De uitnodiging van %(oneUser)s is ingetrokken", @@ -648,7 +648,7 @@ "Failed to remove a user from the summary of %(groupId)s": "Verwijderen van gebruiker uit het overzicht van %(groupId)s is mislukt", "The user '%(displayName)s' could not be removed from the summary.": "De gebruiker ‘%(displayName)s’ kon niet uit het overzicht verwijderd worden.", "Failed to update community": "Bijwerken van gemeenschap is mislukt", - "Unable to accept invite": "Kan de uitnodiging niet aanvaarden", + "Unable to accept invite": "Kan de uitnodiging niet aannemen", "Unable to reject invite": "Kan de uitnodiging niet weigeren", "Leave Community": "Gemeenschap verlaten", "Leave %(groupName)s?": "%(groupName)s verlaten?", @@ -741,7 +741,7 @@ "Send Custom Event": "Aangepaste gebeurtenis versturen", "Advanced notification settings": "Geavanceerde meldingsinstellingen", "delete the alias.": "verwijder de bijnaam.", - "To return to your account in future you need to set a password": "Tenzij u een wachtwoord instelt zult u uw account niet meer kunnen benaderen", + "To return to your account in future you need to set a password": "Om uw account te kunnen blijven gebruiken dient u een wachtwoord in te stellen", "Forget": "Vergeten", "You cannot delete this image. (%(code)s)": "U kunt deze afbeelding niet verwijderen. (%(code)s)", "Cancel Sending": "Versturen annuleren", @@ -830,7 +830,7 @@ "Riot does not know how to join a room on this network": "Riot weet niet hoe het moet deelnemen aan een gesprek op dit netwerk", "Mentions only": "Alleen vermeldingen", "Wednesday": "Woensdag", - "You can now return to your account after signing out, and sign in on other devices.": "U kunt nu terugkeren naar uw account nadat u zich heeft afgemeld, en u aanmelden op andere apparaten.", + "You can now return to your account after signing out, and sign in on other devices.": "Na afmelding kunt u terugkeren tot uw account, en u op andere apparaten aanmelden.", "Enable email notifications": "E-mailmeldingen inschakelen", "Event Type": "Gebeurtenistype", "Download this file": "Dit bestand downloaden", @@ -863,10 +863,10 @@ "Yes, I want to help!": "Ja, ik wil helpen!", "Popout widget": "Widget in nieuw venster openen", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Kan de gebeurtenis waarop gereageerd was niet laden. Wellicht bestaat die niet, of heeft u geen toestemming die te bekijken.", - "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.": "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Hierdoor zal uw account alle gesprekken waaraan deze deelneemt verlaten, en worden de accountgegevens verwijderd van de identiteitsserver. Deze actie is onomkeerbaar.", - "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.": "Het deactiveren van uw account zal er standaard niet voor zorgen dat de berichten die u heeft verstuurd vergeten worden. Als u wilt dat wij de berichten vergeten, vinkt u het vakje hieronder aan.", + "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.": "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Hierdoor zal uw account alle gesprekken waaraan ze deelneemt verlaten, en worden de accountgegevens verwijderd van de identiteitsserver. Deze stap is onomkeerbaar.", + "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.": "Het sluiten van uw account maakt op zich niet dat wij de door u verstuurde berichten vergeten. Als u wilt dat wij uw berichten vergeten, vink dan het vakje hieronder aan.", "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.": "De zichtbaarheid van berichten in Matrix is zoals bij e-mails. Het vergeten van uw berichten betekent dat berichten die u heeft verstuurd niet meer gedeeld worden met nieuwe of ongeregistreerde gebruikers, maar geregistreerde gebruikers die al toegang hebben tot deze berichten zullen alsnog toegang hebben tot hun eigen kopie ervan.", - "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)": "Vergeet alle berichten die ik heb verstuurd wanneer mijn account gedeactiveerd is (Let op: dit zal er voor zorgen dat toekomstige gebruikers een onvolledig beeld krijgen van gesprekken)", + "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)": "Vergeet bij het sluiten van mijn account alle door mij verstuurde berichten (Let op: hierdoor zullen gebruikers een onvolledig beeld krijgen van gesprekken)", "To continue, please enter your password:": "Voer uw wachtwoord in om verder te gaan:", "Clear Storage and Sign Out": "Opslag wissen en afmelden", "Send Logs": "Logboek versturen", @@ -877,7 +877,7 @@ "Can't leave Server Notices room": "Kan servermeldingsgesprek niet verlaten", "This room is used for important messages from the Homeserver, so you cannot leave it.": "Dit gesprek is bedoeld voor belangrijke berichten van de thuisserver, dus u kunt het niet verlaten.", "Terms and Conditions": "Gebruiksvoorwaarden", - "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Om de %(homeserverDomain)s-thuisserver te blijven gebruiken, zult u de gebruiksvoorwaarden moeten lezen en aanvaarden.", + "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Om de %(homeserverDomain)s-thuisserver te blijven gebruiken, zult u de gebruiksvoorwaarden moeten bestuderen en aanvaarden.", "Review terms and conditions": "Gebruiksvoorwaarden lezen", "Call in Progress": "Lopend gesprek", "A call is currently being placed!": "Er wordt al een oproep gemaakt!", @@ -989,7 +989,7 @@ "Render simple counters in room header": "Eenvoudige tellers bovenaan het gesprek tonen", "Enable Emoji suggestions while typing": "Emoticons voorstellen tijdens het typen", "Show a placeholder for removed messages": "Vulling tonen voor verwijderde berichten", - "Show join/leave messages (invites/kicks/bans unaffected)": "Berichten over deelnamen en verlatingen tonen (dit heeft geen effect op uitnodigingen, berispingen of verbanningen)", + "Show join/leave messages (invites/kicks/bans unaffected)": "Berichten over toe- en uittredingen tonen (dit heeft geen effect op uitnodigingen, berispingen of verbanningen)", "Show avatar changes": "Veranderingen van avatar tonen", "Show display name changes": "Veranderingen van weergavenamen tonen", "Show read receipts sent by other users": "Door andere gebruikers verstuurde leesbevestigingen tonen", @@ -1107,7 +1107,7 @@ "Language and region": "Taal en regio", "Theme": "Thema", "Account management": "Accountbeheer", - "Deactivating your account is a permanent action - be careful!": "Het deactiveren van uw account kan niet ongedaan gemaakt worden - wees voorzichtig!", + "Deactivating your account is a permanent action - be careful!": "Pas op! Het sluiten van uw account kan niet ongedaan gemaakt worden!", "General": "Algemeen", "Legal": "Wettelijk", "Credits": "Met dank aan", @@ -1122,7 +1122,7 @@ "Timeline": "Tijdslijn", "Room list": "Gesprekslijst", "Autocomplete delay (ms)": "Vertraging voor automatisch aanvullen (ms)", - "Accept all %(invitedRooms)s invites": "Alle %(invitedRooms)s-uitnodigingen aanvaarden", + "Accept all %(invitedRooms)s invites": "Alle %(invitedRooms)s de uitnodigingen aannemen", "Key backup": "Sleutelback-up", "Security & Privacy": "Veiligheid & privacy", "Missing media permissions, click the button below to request.": "Mediatoestemmingen ontbreken, klik op de knop hieronder om deze aan te vragen.", @@ -1210,7 +1210,7 @@ "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verifieer deze gebruiker om hem/haar als vertrouwd te markeren. Gebruikers vertrouwen geeft u extra gemoedsrust bij het gebruik van eind-tot-eind-versleutelde berichten.", "Waiting for partner to confirm...": "Wachten op bevestiging van partner…", "Incoming Verification Request": "Inkomend verificatieverzoek", - "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.": "U heeft voorheen Riot op %(host)s gebruikt met lui laden van leden ingeschakeld. In deze versie is lui laden uitgeschakeld. Omdat de lokale cache niet compatibel is tussen deze twee instellingen, moet Riot uw account opnieuw synchroniseren.", + "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.": "U heeft voorheen Riot op %(host)s gebruikt met lui laden van leden ingeschakeld. In deze versie is lui laden uitgeschakeld. De lokale cache is niet compatibel tussen deze twee instellingen, zodat Riot uw account moet hersynchroniseren.", "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.": "Indien de andere versie van Riot nog open staat in een ander tabblad kunt u dat beter sluiten, want het geeft problemen als Riot op dezelfde host gelijktijdig met lui laden ingeschakeld en uitgeschakeld draait.", "Incompatible local cache": "Incompatibele lokale cache", "Clear cache and resync": "Cache wissen en hersynchroniseren", @@ -1261,18 +1261,18 @@ "Set a new status...": "Stel een nieuwe status in…", "Hide": "Verbergen", "This homeserver would like to make sure you are not a robot.": "Deze thuisserver wil graag weten of u geen robot bent.", - "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "U kunt de aangepaste serveropties gebruiken om u aan te melden bij andere Matrix-servers, door een andere thuisserver-URL op te geven. Dit biedt u de mogelijkheid om deze toepassing te gebruiken met een bestaande Matrix-account op een andere thuisserver.", - "Please review and accept all of the homeserver's policies": "Gelieve het beleid van de thuisserver te doornemen en aanvaarden", - "Please review and accept the policies of this homeserver:": "Gelieve het beleid van deze thuisserver te doornemen en aanvaarden:", + "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "Middels de aangepaste serveropties kunt u zich aanmelden bij andere Matrix-servers, door een andere thuisserver-URL op te geven. Zo kunt u deze toepassing met een bestaande Matrix-account op een andere thuisserver gebruiken.", + "Please review and accept all of the homeserver's policies": "Gelieve het beleid van de thuisserver door te nemen en te aanvaarden", + "Please review and accept the policies of this homeserver:": "Gelieve het beleid van deze thuisserver door te nemen en te aanvaarden:", "Your Modular server": "Uw Modular-server", "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of modular.im.": "Voer de locatie van uw Modular-thuisserver in. Deze kan uw eigen domeinnaam gebruiken, of een subdomein van modular.im zijn.", "Server Name": "Servernaam", "The username field must not be blank.": "Het gebruikersnaamveld mag niet leeg zijn.", "Username": "Gebruikersnaam", - "Not sure of your password? Set a new one": "Onzeker over uw wachtwoord? Stel er een nieuw in", + "Not sure of your password? Set a new one": "Onzeker over uw wachtwoord? Stel een nieuw in", "Sign in to your Matrix account on %(serverName)s": "Aanmelden met uw Matrix-account op %(serverName)s", "Change": "Wijzigen", - "Create your Matrix account on %(serverName)s": "Maak uw Matrix-account aan op %(serverName)s", + "Create your Matrix account on %(serverName)s": "Maak uw Matrix-account op %(serverName)s aan", "Email (optional)": "E-mailadres (optioneel)", "Phone (optional)": "Telefoonnummer (optioneel)", "Confirm": "Bevestigen", @@ -1444,7 +1444,7 @@ "Edit message": "Bericht bewerken", "View Servers in Room": "Servers in gesprek bekijken", "Unable to validate homeserver/identity server": "Kan thuis-/identiteitsserver niet valideren", - "Sign in to your Matrix account on ": "Meld u aan met uw Matrix-account op ", + "Sign in to your Matrix account on ": "Meld u met uw Matrix-account op aan", "Use an email address to recover your account": "Gebruik een e-mailadres om uw account te herstellen", "Enter email address (required on this homeserver)": "Voer een e-mailadres in (vereist op deze thuisserver)", "Doesn't look like a valid email address": "Dit lijkt geen geldig e-mailadres", @@ -1457,7 +1457,7 @@ "Doesn't look like a valid phone number": "Dit lijkt geen geldig telefoonnummer", "Enter username": "Voer gebruikersnaam in", "Some characters not allowed": "Sommige tekens zijn niet toegestaan", - "Create your Matrix account on ": "Maak uw Matrix-account aan op ", + "Create your Matrix account on ": "Maak uw Matrix-account op aan", "Add room": "Gesprek toevoegen", "Your profile": "Uw profiel", "Your Matrix account on ": "Uw Matrix-account op ", @@ -1485,7 +1485,7 @@ "You can reset your password, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "U kunt uw wachtwoord opnieuw instellen, maar sommige functies zullen pas beschikbaar komen wanneer de identiteitsserver weer online is. Als u deze waarschuwing blijft zien, controleer dan uw configuratie of neem contact op met een serverbeheerder.", "You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "U kunt zich aanmelden, maar sommige functies zullen pas beschikbaar zijn wanneer de identiteitsserver weer online is. Als u deze waarschuwing blijft zien, controleer dan uw configuratie of neem contact op met een systeembeheerder.", "Log in to your new account.": "Meld u aan met uw nieuwe account.", - "You can now close this window or log in to your new account.": "U kunt dit venster nu sluiten, of u aanmelden met uw nieuwe account.", + "You can now close this window or log in to your new account.": "U kunt dit venster nu sluiten, of u met uw nieuwe account aanmelden.", "Registration Successful": "Registratie geslaagd", "Upload all": "Alles versturen", "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Uw nieuwe account (%(newAccountId)s) is geregistreerd, maar u bent reeds aangemeld met een andere account (%(loggedInUserId)s).", @@ -1509,7 +1509,7 @@ "Resend removal": "Verwijdering opnieuw versturen", "Failed to re-authenticate due to a homeserver problem": "Opnieuw aanmelden is mislukt wegens een probleem met de thuisserver", "Failed to re-authenticate": "Opnieuw aanmelden is mislukt", - "Enter your password to sign in and regain access to your account.": "Voer uw wachtwoord in om u aan te melden en opnieuw toegang te verkrijgen tot uw account.", + "Enter your password to sign in and regain access to your account.": "Voer uw wachtwoord in om u aan te melden en toegang tot uw account te herkrijgen.", "Forgotten your password?": "Wachtwoord vergeten?", "You're signed out": "U bent afgemeld", "Clear personal data": "Persoonlijke gegevens wissen", @@ -1521,9 +1521,9 @@ "Terms of Service": "Gebruiksvoorwaarden", "Service": "Dienst", "Summary": "Samenvatting", - "Sign in and regain access to your account.": "Meld u aan en verkrijg opnieuw toegang tot uw account.", - "You cannot sign in to your account. Please contact your homeserver admin for more information.": "U kunt zich niet aanmelden met uw account. Neem contact op met de beheerder van uw thuisserver voor meer informatie.", - "This account has been deactivated.": "Deze account is gedeactiveerd.", + "Sign in and regain access to your account.": "Meld u aan en herkrijg toegang tot uw account.", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "U kunt zich niet aanmelden met uw account. Neem voor meer informatie contact op met de beheerder van uw thuisserver.", + "This account has been deactivated.": "Deze account is gesloten.", "Messages": "Berichten", "Actions": "Acties", "Displays list of commands with usages and descriptions": "Toont een lijst van beschikbare opdrachten, met hun gebruiken en beschrijvingen", @@ -1539,7 +1539,7 @@ "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "De verbinding met uw identiteitsserver verbreken zal ertoe leiden dat u niet door andere gebruikers gevonden zal kunnen worden, en dat u anderen niet via e-mail of telefoon zal kunnen uitnodigen.", "Integration Manager": "Integratiebeheerder", "Discovery": "Ontdekking", - "Deactivate account": "Account deactiveren", + "Deactivate account": "Account sluiten", "Always show the window menu bar": "De venstermenubalk altijd tonen", "Unable to revoke sharing for email address": "Kan delen voor dit e-mailadres niet intrekken", "Unable to share email address": "Kan e-mailadres niet delen", @@ -1611,11 +1611,11 @@ "Strikethrough": "Doorstreept", "Code block": "Codeblok", "An error (%(errcode)s) was returned while trying to validate your invite. You could try to pass this information on to a room admin.": "Er is een fout opgetreden (%(errcode)s) bij het valideren van uw uitnodiging. U kunt deze informatie doorgeven aan een gespreksbeheerder.", - "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "Deze uitnodiging tot %(roomName)s was verstuurd naar %(email)s, wat niet aan uw account gekoppeld is", - "Link this email with your account in Settings to receive invites directly in Riot.": "Koppel dit e-mailadres aan uw account in de instellingen om uitnodigingen automatisch te ontvangen in Riot.", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "Deze uitnodiging tot %(roomName)s was verstuurd naar %(email)s, dat niet aan uw account gekoppeld is", + "Link this email with your account in Settings to receive invites directly in Riot.": "Koppel in de instellingen dit e-mailadres aan uw account om uitnodigingen direct in Riot te ontvangen.", "This invite to %(roomName)s was sent to %(email)s": "Deze uitnodiging tot %(roomName)s was verstuurd naar %(email)s", - "Use an identity server in Settings to receive invites directly in Riot.": "Gebruik een identiteitsserver in de instellingen om uitnodigingen automatisch te ontvangen in Riot.", - "Share this email in Settings to receive invites directly in Riot.": "Deel dit e-mailadres in de instellingen om uitnodigingen automatisch te ontvangen in Riot.", + "Use an identity server in Settings to receive invites directly in Riot.": "Gebruik in de instellingen een identiteitsserver om uitnodigingen direct in Riot te ontvangen.", + "Share this email in Settings to receive invites directly in Riot.": "Deel in de instellingen dit e-mailadres om uitnodigingen direct in Riot te ontvangen.", "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Gebruik een identiteitsserver om uit te nodigen op e-mailadres. Gebruik de standaardserver (%(defaultIdentityServerName)s) of beheer de server in de Instellingen.", "Use an identity server to invite by email. Manage in Settings.": "Gebruik een identiteitsserver om anderen uit te nodigen via e-mail. Beheer de server in de Instellingen.", "Please fill why you're reporting.": "Gelieve aan te geven waarom u deze melding indient.", @@ -1623,8 +1623,8 @@ "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Dit bericht melden zal zijn unieke ‘gebeurtenis-ID’ versturen naar de beheerder van uw thuisserver. Als de berichten in dit gesprek versleuteld zijn, zal de beheerder van uw thuisserver het bericht niet kunnen lezen, noch enige bestanden of afbeeldingen zien.", "Send report": "Rapport versturen", "Report Content": "Inhoud melden", - "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Stel een e-mailadres in voor accountherstel. Gebruik optioneel een e-mailadres of telefoonnummer om vindbaar te zijn voor bestaande contacten.", - "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Stel een e-mailadres in voor accountherstel. Gebruik optioneel een e-mailadres om vindbaar te zijn voor bestaande contacten.", + "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Stel een e-mailadres voor accountherstel in. Gebruik eventueel een e-mailadres of telefoonnummer om vindbaar te zijn voor bestaande contacten.", + "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Stel een e-mailadres voor accountherstel in. Gebruik eventueel een e-mailadres om vindbaar te zijn voor bestaande contacten.", "Enter your custom homeserver URL What does this mean?": "Voer uw aangepaste thuisserver-URL in Wat betekent dit?", "Enter your custom identity server URL What does this mean?": "Voer uw aangepaste identiteitsserver-URL in Wat betekent dit?", "Explore": "Ontdekken", @@ -1864,7 +1864,7 @@ "If disabled, messages from encrypted rooms won't appear in search results.": "Dit moet aan staan om te kunnen zoeken in versleutelde gesprekken.", "Indexed rooms:": "Geïndexeerde gesprekken:", "Cross-signing and secret storage are enabled.": "Kruiselings ondertekenen en sleutelopslag zijn ingeschakeld.", - "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Uw account heeft een identiteit voor kruiselings ondertekenen in de sleutelopslag, maar deze is nog niet vertrouwd door de huidige sessie.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Uw account heeft een identiteit voor kruiselings ondertekenen in de sleutelopslag, maar die wordt nog niet vertrouwd door de huidige sessie.", "Cross-signing and secret storage are not yet set up.": "Kruiselings ondertekenen en sleutelopslag zijn nog niet ingesteld.", "Bootstrap cross-signing and secret storage": "Kruiselings ondertekenen en sleutelopslag instellen", "Reset cross-signing and secret storage": "Kruiselings ondertekenen en sleutelopslag opnieuw instellen", @@ -1898,7 +1898,7 @@ "Homeserver feature support:": "Functies ondersteund door thuisserver:", "exists": "bestaat", "Sign In or Create Account": "Meld u aan of maak een account aan", - "Use your account or create a new one to continue.": "Gebruik uw bestaande account of maak er een nieuwe aan om verder te gaan.", + "Use your account or create a new one to continue.": "Gebruik uw bestaande account of maak een nieuwe aan om verder te gaan.", "Create Account": "Account aanmaken", "Displays information about a user": "Geeft informatie weer over een gebruiker", "Order rooms by name": "Gesprekken sorteren op naam", @@ -1938,7 +1938,7 @@ "Send as message": "Versturen als bericht", "Failed to connect to integration manager": "Verbinding met integratiebeheerder is mislukt", "Waiting for %(displayName)s to accept…": "Wachten tot %(displayName)s aanvaardt…", - "Accepting…": "Aanvaarden…", + "Accepting…": "Toestaan…", "Start Verification": "Verificatie beginnen", "Messages in this room are end-to-end encrypted.": "De berichten in dit gesprek worden eind-tot-eind-versleuteld.", "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Uw berichten zijn beveiligd, en enkel de ontvanger en u hebben de unieke sleutels om ze te ontsleutelen.", @@ -1995,7 +1995,7 @@ "You cancelled": "U heeft geannuleerd", "%(name)s declined": "%(name)s heeft geweigerd", "%(name)s cancelled": "%(name)s heeft geannuleerd", - "Accepting …": "Aanvaarden…", + "Accepting …": "Toestaan…", "Declining …": "Weigeren…", "%(name)s wants to verify": "%(name)s wil verifiëren", "You sent a verification request": "U heeft een verificatieverzoek verstuurd", @@ -2061,14 +2061,14 @@ "You added a new session '%(displayName)s', which is requesting encryption keys.": "U heeft een nieuwe sessie ‘%(displayName)s’ toegevoegd, die om versleutelingssleutels vraagt.", "Your unverified session '%(displayName)s' is requesting encryption keys.": "Uw ongeverifieerde sessie ‘%(displayName)s’ vraagt om versleutelingssleutels.", "Loading session info...": "Sessie-info wordt geladen…", - "Your account is not secure": "Uw account is niet veilig", + "Your account is not secure": "Uw account is onveilig", "Your password": "Uw wachtwoord", "This session, or the other session": "Deze sessie, of de andere sessie", "The internet connection either session is using": "De internetverbinding gebruikt door een van de sessies", "We recommend you change your password and recovery key in Settings immediately": "We raden u aan onmiddellijk uw wachtwoord en herstelsleutel te wijzigen in de instellingen", "New session": "Nieuwe sessie", "Use this session to verify your new one, granting it access to encrypted messages:": "Gebruik deze sessie om uw nieuwe sessie te verifiëren, waardoor deze laatste toegang verkrijgt tot versleutelde berichten:", - "If you didn’t sign in to this session, your account may be compromised.": "Als u zich niet heeft aangemeld bij deze sessie, is uw account mogelijk gecompromitteerd.", + "If you didn’t sign in to this session, your account may be compromised.": "Als u zich niet heeft aangemeld bij deze sessie, is uw account wellicht geschonden.", "This wasn't me": "Dat was ik niet", "Automatically invite users": "Gebruikers automatisch uitnodigen", "Upgrade private room": "Privégesprek bijwerken", @@ -2076,7 +2076,7 @@ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Het bijwerken van een gesprek is een gevorderde actie en wordt meestal aanbevolen wanneer een gesprek onstabiel is door fouten, ontbrekende functies of problemen met de beveiliging.", "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Dit heeft meestal enkel een invloed op de manier waarop het gesprek door de server verwerkt wordt. Als u problemen met uw Riot ondervindt, dien dan een foutmelding in.", "You'll upgrade this room from to .": "U werkt dit gesprek bij van naar .", - "This will allow you to return to your account after signing out, and sign in on other sessions.": "Dit biedt u de mogelijkheid om terug te keren naar uw account nadat u zich heeft afgemeld, en om u aan te melden bij andere sessies.", + "This will allow you to return to your account after signing out, and sign in on other sessions.": "Daardoor kunt u na afmelding terugkeren tot uw account, en u bij andere sessies aanmelden.", "You are currently blacklisting unverified sessions; to send messages to these sessions you must verify them.": "U blokkeert momenteel niet-geverifieerde sessies; om berichten te sturen naar deze sessies moet u ze verifiëren.", "We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We raden u aan om het verificatieproces voor elke sessie te doorlopen om te bevestigen dat ze aan hun rechtmatige eigenaar toebehoren, maar u kunt het bericht ook opnieuw versturen zonder verificatie indien u dit wenst.", "Room contains unknown sessions": "Gesprek bevat onbekende sessies", @@ -2122,8 +2122,8 @@ "Go Back": "Terugkeren", "Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Door uw wachtwoord te wijzigen stelt u alle eind-tot-eind-versleutelingssleutels op al uw sessies opnieuw in, waardoor uw versleutelde gespreksgeschiedenis onleesbaar wordt. Stel sleutelback-up in of schrijf uw gesprekssleutels van een andere sessie weg vooraleer u een nieuw wachtwoord instelt.", "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "U bent afgemeld bij al uw sessies en zult geen pushberichten meer ontvangen. Meld u op elk apparaat opnieuw aan om meldingen opnieuw in te schakelen.", - "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Verkrijg opnieuw de toegang tot uw account en herstel de versleutelingssleutels die in deze sessie opgeslagen zijn. Hierzonder zult u niet al uw beveiligde berichten in al uw sessies kunnen lezen.", - "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Let op: uw persoonlijke gegevens (versleutelingssleutels inbegrepen) worden nog steeds opgeslagen in deze sessie. Wis ze wanneer u klaar bent met deze sessie, of wanneer u zich wilt aanmelden met een andere account.", + "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Herwin toegang tot uw account en herstel de tijdens deze sessie opgeslagen versleutelingssleutels, zonder welke sommige van uw beveiligde berichten in al uw sessies onleesbaar zijn.", + "Warning: Your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.": "Let op: uw persoonlijke gegevens (waaronder versleutelingssleutels) zijn nog steeds opgeslagen in deze sessie. Wis ze wanneer u klaar bent met deze sessie, of wanneer u zich wilt aanmelden met een andere account.", "Command Autocomplete": "Opdrachten automatisch aanvullen", "DuckDuckGo Results": "DuckDuckGo-resultaten", "Sender session information": "Sessie-informatie van afzender", @@ -2177,7 +2177,7 @@ "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bevestig uw identiteit met Eenmalige Aanmelding om dit telefoonnummer toe te voegen.", "Confirm adding phone number": "Bevestig toevoegen van het telefoonnummer", "Click the button below to confirm adding this phone number.": "Klik op de knop hieronder om het toevoegen van dit telefoonnummer te bevestigen.", - "Review Sessions": "Sessieverificatie", + "Review Sessions": "Sessies nazien", "If you cancel now, you won't complete your operation.": "Als u de operatie afbreekt kunt u haar niet voltooien.", "Review where you’re logged in": "Kijk na waar u aangemeld bent", "New login. Was this you?": "Nieuwe aanmelding - was u dat?", @@ -2200,5 +2200,14 @@ "Verify your other session using one of the options below.": "Verifieer uw andere sessie op een van onderstaande wijzen.", "Manually Verify by Text": "Handmatig middels een tekst", "Interactively verify by Emoji": "Interactief middels emojis", - "Support adding custom themes": "Sta maatwerkthema's toe" + "Support adding custom themes": "Sta maatwerkthema's toe", + "Opens chat with the given user": "Start een tweegesprek met die gebruiker", + "Sends a message to the given user": "Zendt die gebruiker een bericht", + "Font scaling": "Lettergrootte", + "Use the improved room list (in development - refresh to apply changes)": "Gebruik de verbeterde gesprekslijst (in ontwikkeling - ververs om veranderingen te zien)", + "Verify all your sessions to ensure your account & messages are safe": "Controleer al uw sessies om zeker te zijn dat uw account & berichten veilig zijn", + "Verify the new login accessing your account: %(name)s": "Verifieer de nieuwe aanmelding op uw account: %(name)s", + "Confirm your account deactivation by using Single Sign On to prove your identity.": "Bevestig uw intentie deze account te sluiten door met Single Sign On uw identiteit te bewijzen.", + "Are you sure you want to deactivate your account? This is irreversible.": "Weet u zeker dat u uw account wil sluiten? Dit is onomkeerbaar.", + "Confirm account deactivation": "Bevestig accountsluiting" } From 2a2967d88b386696f42942da0b25af861cb80ff9 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sat, 23 May 2020 18:30:36 +0000 Subject: [PATCH 301/399] Translated using Weblate (Esperanto) Currently translated at 100.0% (2322 of 2322 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 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index f201d5a117..983ba9e7db 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -385,7 +385,7 @@ "Communities": "Komunumoj", "Home": "Hejmo", "Could not connect to the integration server": "Malsukcesis konektiĝi al la kuniga servilo", - "Manage Integrations": "Administri integrojn", + "Manage Integrations": "Administri kunigojn", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s%(count)s-foje aliĝis", "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)saliĝis", @@ -882,7 +882,7 @@ "Delete Backup": "Forigi savkopion", "Language and region": "Lingvo kaj regiono", "Theme": "Haŭto", - "General": "Ĝenerala", + "General": "Ĝeneralaj", "In reply to ": "Responde al ", "Share Message": "Diskonigi", "Whether or not you're logged in (we don't record your username)": "Ĉu vi salutis aŭ ne (ni ne registras vian uzantonomon)", @@ -974,9 +974,9 @@ "Room list": "Ĉambrolisto", "Ignored users": "Malatentaj uzantoj", "Key backup": "Sekurkopio de ŝlosilo", - "Security & Privacy": "Sekureco & Privateco", + "Security & Privacy": "Sekureco kaj Privateco", "Voice & Video": "Voĉo kaj vido", - "Room information": "Ĉambraj informoj", + "Room information": "Informoj pri ĉambro", "Internal room ID:": "Ena ĉambra identigilo:", "Room version": "Ĉambra versio", "Room version:": "Ĉambra versio:", @@ -998,7 +998,7 @@ "Remove messages": "Forigi mesaĝojn", "Notify everyone": "Sciigi ĉiujn", "Muted Users": "Silentigitaj uzantoj", - "Roles & Permissions": "Roloj & Permesoj", + "Roles & Permissions": "Roloj kaj Permesoj", "Enable encryption?": "Ĉu ŝalti ĉifradon?", "Share Link to User": "Kunhavigi ligilon al uzanto", "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Vidita de %(displayName)s (%(userName)s) je %(dateTime)s", @@ -1017,7 +1017,7 @@ "To continue, please enter your password:": "Por daŭrigi, bonvoluenigi vian pasvorton:", "Updating Riot": "Ĝisdatigante Riot", "Go back": "Reen iri", - "Room Settings - %(roomName)s": "Ĉambraj agordoj — %(roomName)s", + "Room Settings - %(roomName)s": "Agordoj de ĉambro – %(roomName)s", "Failed to upgrade room": "Malsukcesis gradaltigi ĉambron", "Refresh": "Aktualigi", "Checking...": "Kontrolante…", @@ -1025,7 +1025,7 @@ "Share User": "Kunhavigi uzanton", "Share Community": "Kunhavigi komunumon", "Share Room Message": "Kunhavigi ĉambran mesaĝon", - "COPY": "KOPIO", + "COPY": "KOPII", "Next": "Sekva", "Clear status": "Vakigi staton", "Update status": "Ĝisdatigi staton", @@ -1386,7 +1386,7 @@ "The conversation continues here.": "La interparolo pluas ĉi tie.", "This room has been replaced and is no longer active.": "Ĉi tiu ĉambro estas anstataŭita, kaj ne plu aktivas.", "Loading room preview": "Preparas antaŭrigardon al la ĉambro", - "Only room administrators will see this warning": "Nur ĉambraj administrantoj vidos ĉi tiun averton", + "Only room administrators will see this warning": "Nur administrantoj de ĉambro vidos ĉi tiun averton", "Error updating flair": "Eraris ĝisdatigo de etikedo", "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Eraris ĝisdatigo de etikedo por ĉi tiu ĉambro. Aŭ la servilo ne permesas ĝin, aŭ dumtempa eraro okazis.", "Showing flair for these communities:": "Montras etikedojn de la jenaj komunumoj:", @@ -2055,7 +2055,7 @@ "%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s nuligis la kontrolon. Rekomencu ĝin de ĝia profilo.", "You cancelled verification. Start verification again from their profile.": "Vi nuligis la kontrolon. Rekomencu ĝin de ĝia profilo.", "Encryption enabled": "Ĉifrado estas ŝaltita", - "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Mesaĝojn en ĉi tiu ĉambro estas tutvoje ĉifrataj. Eksciu plion kaj kontrolu ĉi tiun uzanton el ĝia profilo.", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Mesaĝoj en ĉi tiu ĉambro estas tutvoje ĉifrataj. Eksciu plion kaj kontrolu ĉi tiun uzanton per ĝia profilo.", "Encryption not enabled": "Ĉifrado ne estas ŝaltita", "The encryption used by this room isn't supported.": "La ĉifro uzata de ĉi tiu ĉambro ne estas subtenata.", "You have ignored this user, so their message is hidden. Show anyways.": "Vi malatentis ĉi tiun uzanton, ĝia mesaĝo estas do kaŝita. Tamen montri.", From 4395c59eb1a9919582e5983b98b0b3a7d57e35be Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Mon, 25 May 2020 04:38:28 +0000 Subject: [PATCH 302/399] Translated using Weblate (Finnish) Currently translated at 96.1% (2232 of 2322 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 | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 05bf538ff9..66c07d3722 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -2276,5 +2276,42 @@ "Select room from the room list": "Valitse huone huoneluettelosta", "Previous/next unread room or DM": "Edellinen/seuraava lukematon huone tai yksityisviesti", "Previous/next room or DM": "Edellinen/seuraava huone tai yksityisviesti", - "Toggle this dialog": "Tämä valintaikkuna päälle/pois" + "Toggle this dialog": "Tämä valintaikkuna päälle/pois", + "Use the improved room list (in development - refresh to apply changes)": "Käytä parannettua huoneluetteloa (kehitysversio — päivitä sivu ottaaksesi muutokset käyttöön)", + "Start verification again from the notification.": "Aloita varmennus uudelleen ilmoituksesta.", + "Start verification again from their profile.": "Aloita varmennus uudelleen hänen profiilista.", + "Verification timed out.": "Varmennuksessa kesti liikaa.", + "You cancelled verification on your other session.": "Peruutit varmennuksen toisessa istunnossasi.", + "%(displayName)s cancelled verification.": "%(displayName)s peruutti varmennuksen.", + "You cancelled verification.": "Peruutit varmennuksen.", + "Verification cancelled": "Varmennus peruutettu", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Tämän huoneen viestit ovat salattuja osapuolten välisellä salauksella. Lue lisää ja varmenna tämä käyttäjä hänen profiilistaan.", + "Enter the name of a new server you want to explore.": "Syötä sen uuden palvelimen nimi, jota hauat tutkia.", + "%(networkName)s rooms": "Verkon %(networkName)s huoneet", + "Destroy cross-signing keys?": "Tuhoa ristivarmennuksen avaimet?", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Ristivarmennuksen avainten tuhoamista ei voi kumota. Jokainen, jonka olet varmentanut, tulee näkemään turvallisuushälytyksiä. Et todennäköisesti halua tehdä tätä, ellet ole hukannut kaikkia laitteitasi, joista pystyt ristivarmentamaan.", + "Clear cross-signing keys": "Tyhjennä ristivarmennuksen avaimet", + "Enable end-to-end encryption": "Ota osapuolten välinen salaus käyttöön", + "To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Jotta tähän istuntoon voitaisiin luottaa, tarkista, että käyttäjän asetuksissa näkyvä avain täsmää alapuolella olevaan avaimeen:", + "To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Jotta tähän istuntoon voitaisiin luottaa, ota yhteyttä sen omistajaan jotain muuta kautta (esim. kasvotusten tai puhelimitse) ja kysy, että täsmääkö hänen käyttäjäasetuksissa näkemänsä istunnon avain alla olevaan:", + "Session key": "Istunnon tunnus", + "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this session and you probably want to press the blacklist button instead.": "Jos se täsmää, paina varmennuspainiketta alapuolella. Jos se ei täsmää, joku häiritsee tätä istuntoa ja haluat luultavasti painaa estä -painiketta sen sijaan.", + "Verification Requests": "Varmennuspyynnöt", + "Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Tämän käyttäjän varmentaminen merkitsee hänen istuntonsa luotetuksi, ja myös merkkaa sinun istuntosi luotetuksi hänen laitteissaan.", + "Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Varmenna tämä laite merkataksesi se luotetuksi. Tähän laitteeseen luottaminen antaa sinulle ja muille käyttäjille ylimääräistä mielenrauhaa, kun käytätte osapuolten välistä salausta.", + "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Tämän laitteen varmentaminen merkkaa sen luotetuksi, ja sinut varmentaneet käyttäjät luottavat automaattisesti tähän laitteeseen.", + "Confirm to continue": "Haluan jatkaa", + "Click the button below to confirm your identity.": "Paina alapuolella olevaa painiketta varmistaaksesi identiteettisi.", + "We couldn't create your DM. Please check the users you want to invite and try again.": "Emme onnistuneet luomaan yksityisviestiä. Tarkista, että kutsumasi henkilöt haluavat kutsusi ja yritä uudelleen.", + "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Seuraavat käyttäjät eivät välttämättä ole olemassa tai ne ovat epäkelpoja, joten niitä ei voida kutsua: %(csvNames)s", + "Recently Direct Messaged": "Viimeaikaiset yksityisviestit", + "Start a conversation with someone using their name, username (like ) or email address.": "Aloita keskustelu jonkun kanssa käyttäen hänen nimeä, käyttäjätunnus (kuten ) tai sähköpostiosoitetta.", + "Invite someone using their name, username (like ), email address or share this room.": "Kutsu tähän huoneeseen käyttäen nimeä, käyttäjätunnusta (kuten ), sähköpostiosoitetta tai jaa tämä huone.", + "Your unverified session '%(displayName)s' is requesting encryption keys.": "Varmentamaton istuntosi '%(displayName)s' pyytää salausavaimia.", + "Riot encountered an error during upload of:": "Riot kohtasi virheen lähettäessään:", + "Upload completed": "Lähetys valmis", + "Cancelled signature upload": "Allekirjoituksen lähetys peruutettu", + "Unable to upload": "Lähettäminen ei ole mahdollista", + "Signature upload success": "Allekirjoituksen lähettäminen onnistui", + "Signature upload failed": "Allekirjoituksen lähettäminen epäonnistui" } From 0ef0f81fc66865ddb9f1a40842032c189d20ffbe Mon Sep 17 00:00:00 2001 From: MamasLT Date: Sat, 23 May 2020 23:33:18 +0000 Subject: [PATCH 303/399] Translated using Weblate (Lithuanian) Currently translated at 61.7% (1432 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 201 ++++++++++++++++++++++++++++++++++----- 1 file changed, 176 insertions(+), 25 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 905ac59c0d..bb4f6cc7a4 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -149,7 +149,7 @@ "remove %(name)s from the directory.": "pašalinti %(name)s iš katalogo.", "You can now return to your account after signing out, and sign in on other devices.": "Po atsijungimo galite grįžti prie savo paskyros ir prisijungti kituose įrenginiuose.", "Continue": "Tęsti", - "Enable email notifications": "Įjungti pranešimus el. paštu", + "Enable email notifications": "Įjungti el. pašto pranešimus", "Event Type": "Įvykio tipas", "No rooms to show": "Nėra kambarių rodymui", "Add rooms to this community": "Įtraukti kambarius į šią bendruomenę", @@ -216,7 +216,7 @@ "Failed to add the following rooms to %(groupId)s:": "Nepavyko pridėti šių kambarių į %(groupId)s:", "Riot does not have permission to send you notifications - please check your browser settings": "Riot neturi leidimo siųsti jums pranešimus - patikrinkite savo naršyklės nustatymus", "Riot was not given permission to send notifications - please try again": "Riot nebuvo suteiktas leidimas siųsti pranešimus - bandykite dar kartą", - "Unable to enable Notifications": "Nepavyko įjungti Pranešimus", + "Unable to enable Notifications": "Nepavyko įjungti pranešimų", "This email address was not found": "Šis el. pašto adresas nebuvo rastas", "Admin": "Administratorius", "Start a chat": "Pradėti pokalbį", @@ -523,9 +523,9 @@ "%(senderName)s changed the pinned messages for the room.": "%(senderName)s pakeitė prisegtas kambario žinutes.", "Sorry, your homeserver is too old to participate in this room.": "Atleiskite, jūsų serverio versija yra per sena dalyvauti šiame kambaryje.", "Please contact your homeserver administrator.": "Prašome susisiekti su savo serverio administratoriumi.", - "Enable inline URL previews by default": "Įjungti tiesiogines URL nuorodų peržiūras pagal numatymą", + "Enable inline URL previews by default": "Įjungti URL nuorodų peržiūras kaip numatytasias", "Enable URL previews for this room (only affects you)": "Įjungti URL nuorodų peržiūras šiame kambaryje (įtakoja tik jus)", - "Enable URL previews by default for participants in this room": "Įjungti URL nuorodų peržiūras pagal numatymą dalyviams šiame kambaryje", + "Enable URL previews by default for participants in this room": "Įjungti URL nuorodų peržiūras kaip numatytasias šiame kambaryje esantiems dalyviams", "Confirm password": "Patvirtinkite slaptažodį", "Demote yourself?": "Pažeminti save?", "Demote": "Pažeminti", @@ -539,10 +539,10 @@ "Who can read history?": "Kas gali skaityti istoriją?", "Only room administrators will see this warning": "Šį įspėjimą matys tik kambario administratoriai", "Remote addresses for this room:": "Nuotoliniai šio kambario adresai:", - "You have enabled URL previews by default.": "Jūs esate įjungę URL nuorodų peržiūras pagal numatymą.", - "You have disabled URL previews by default.": "Jūs esate išjungę URL nuorodų peržiūras pagal numatymą.", - "URL previews are enabled by default for participants in this room.": "URL nuorodų peržiūros yra įjungtos pagal numatymą šio kambario dalyviams.", - "URL previews are disabled by default for participants in this room.": "URL nuorodų peržiūros yra išjungtos pagal numatymą šio kambario dalyviams.", + "You have enabled URL previews by default.": "Jūs esate įjungę URL nuorodų peržiūras kaip numatytasias.", + "You have disabled URL previews by default.": "Jūs esate išjungę URL nuorodų peržiūras kaip numatytasias.", + "URL previews are enabled by default for participants in this room.": "URL nuorodų peržiūros yra įjungtos kaip numatytasios šio kambario dalyviams.", + "URL previews are disabled by default for participants in this room.": "URL nuorodų peržiūros yra išjungtos kaip numatytosios šio kambario dalyviams.", "Invalid file%(extra)s": "Neteisingas failas %(extra)s", "This room is a continuation of another conversation.": "Šis kambarys yra kito pokalbio pratęsimas.", "Click here to see older messages.": "Spustelėkite čia, norėdami matyti senesnes žinutes.", @@ -599,7 +599,7 @@ "This homeserver has hit its Monthly Active User limit.": "Šis serveris pasiekė savo mėnesinį aktyvių naudotojų limitą.", "This homeserver has exceeded one of its resource limits.": "Šis serveris viršijo vieno iš savo išteklių limitą.", "Unable to connect to Homeserver. Retrying...": "Nepavyksta prisijungti prie serverio. Bandoma iš naujo...", - "Enable widget screenshots on supported widgets": "Palaikomuose valdikliuose įjungti valdiklių ekrano kopijas", + "Enable widget screenshots on supported widgets": "Įjungti valdiklių ekrano kopijas palaikomuose valdikliuose", "Export E2E room keys": "Eksportuoti E2E kambario raktus", "Last seen": "Paskutinį kartą matytas", "Unignore": "Nebeignoruoti", @@ -611,7 +611,7 @@ "System Alerts": "Sistemos įspėjimai", "Failed to unban": "Nepavyko atblokuoti", "not specified": "nenurodyta", - "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "Šifruotuose kambariuose, tokiuose kaip šis, URL nuorodų peržiūra pagal numatymą yra išjungta, kad būtų užtikrinta, jog jūsų namų serveris (kuriame yra generuojamos peržiūros) negalės rinkti informacijos apie šiame kambaryje matomas nuorodas.", + "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "Šifruotuose kambariuose, tokiuose kaip šis, URL nuorodų peržiūros pagal numatymą yra išjungtos, kad būtų užtikrinta, jog jūsų serveris (kur yra generuojamos peržiūros) negali rinkti informacijos apie jūsų šiame kambaryje peržiūrėtas nuorodas.", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s pakeitė %(roomName)s avatarą", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s pašalino kambario avatarą.", "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s pakeitė kambario avatarą į ", @@ -641,7 +641,7 @@ "And %(count)s more...|other": "Ir dar %(count)s...", "Existing Call": "Esamas skambutis", "A call is already in progress!": "Skambutis jau vyksta!", - "Default": "Paprastas vartotojas", + "Default": "Numatytas", "Restricted": "Apribotas", "Moderator": "Moderatorius", "Ignores a user, hiding their messages from you": "Ignoruoja vartotoją, slepiant nuo jūsų jo žinutes", @@ -797,7 +797,7 @@ "This room has no topic.": "Šis kambarys neturi temos.", "Sets the room name": "Nustato kambario pavadinimą", "Use an identity server": "Naudoti tapatybės serverį", - "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Norėdami pakviesti nurodydami el. paštą, naudokite tapatybės serverį. Tam, kad būtų naudojamas numatytasis tapatybės serveris %(defaultIdentityServerName)s, spauskite tęsti, arba tvarkykite nustatymuose.", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Norėdami pakviesti nurodydami el. paštą, naudokite tapatybės serverį. Tam, kad toliau būtų naudojamas numatytasis tapatybės serveris %(defaultIdentityServerName)s, spauskite tęsti, arba tvarkykite nustatymuose.", "Use an identity server to invite by email. Manage in Settings.": "Norėdami pakviesti nurodydami el. paštą, naudokite tapatybės serverį. Tvarkykite nustatymuose.", "Joins room with given alias": "Prisijungia prie kambario su nurodytu slapyvardžiu", "Unbans user with given ID": "Atblokuoja vartotoją su nurodytu id", @@ -831,8 +831,8 @@ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s išsiuntė pakvietimą %(targetDisplayName)s prisijungti prie kambario.", "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s padarė būsimą kambario istoriją matomą visiems kambario dalyviams, nuo pat jų prisijungimo.", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s padarė būsimą kambario istoriją matomą nežinomam (%(visibility)s).", - "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s iš %(fromPowerLevel)s į %(toPowerLevel)s", - "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s pakeitė %(powerLevelDiffText)s galios lygį.", + "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s galios lygį iš %(fromPowerLevel)s į %(toPowerLevel)s", + "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s pakeitė %(powerLevelDiffText)s.", "%(displayName)s is typing …": "%(displayName)s rašo …", "%(names)s and %(count)s others are typing …|other": "%(names)s ir %(count)s kiti rašo …", "%(names)s and %(count)s others are typing …|one": "%(names)s ir dar vienas rašo …", @@ -1135,7 +1135,7 @@ "Message deleted": "Žinutė ištrinta", "Message deleted by %(name)s": "Žinutė, ištrinta %(name)s", "Warning: You should only do this on a trusted computer.": "Įspėjimas: Tai atlikite tik saugiame kompiuteryje.", - "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Pasiekite savo saugių žinučių istoriją ir kryžminio parašo tapatybę, naudojamą kitų seansų patvirtinimui, įvesdami savo atgavimo slaptafrazę.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Pasiekite savo saugių žinučių istoriją ir kryžminio pasirašymo tapatybę, naudojamą kitų seansų patvirtinimui, įvesdami savo atgavimo slaptafrazę.", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options.": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus.", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Jei pamiršote savo atgavimo slaptafrazę jūs galite naudoti savo atgavimo raktą arba nustatyti naujus atgavimo nustatymus", "Confirm your identity by entering your account password below.": "Patvirtinkite savo tapatybę žemiau įvesdami savo paskyros slaptažodį.", @@ -1158,7 +1158,7 @@ "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Jūsų slaptažodis buvo sėkmingai pakeistas. Jūs kituose seansuose negausite pranešimų, kol iš naujo prie jų neprisijungsite", "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "El. laiškas buvo išsiųstas į %(emailAddress)s. Kai paspausite jame esančią nuorodą, tada spauskite žemiau.", "Your password has been reset.": "Jūsų slaptažodis buvo iš naujo nustatytas.", - "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų seansų ir toliau nebegausite pranešimų. Tam, kad vėl aktyvuotumėte pranešimus, iš naujo prisijunkite kiekviename įrenginyje.", + "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Jūs buvote atjungtas iš visų seansų ir toliau nebegausite pranešimų. Tam, kad vėl įjungtumėte pranešimus, iš naujo prisijunkite kiekviename įrenginyje.", "Show more": "Rodyti daugiau", "Log in to your new account.": "Prisijunkite prie naujos paskyros.", "Registration Successful": "Registracija sėkminga", @@ -1191,8 +1191,8 @@ "Cryptography": "Kriptografija", "Security & Privacy": "Saugumas ir Privatumas", "Voice & Video": "Garsas ir Vaizdas", - "Enable room encryption": "Įgalinti kambario šifravimą", - "Enable encryption?": "Įgalinti šifravimą?", + "Enable room encryption": "Įjungti kambario šifravimą", + "Enable encryption?": "Įjungti šifravimą?", "Encryption": "Šifravimas", "Once enabled, encryption cannot be disabled.": "Įjungus šifravimą jo nebus galima išjungti.", "Who can access this room?": "Kas turi prieigą prie šio kambario?", @@ -1207,7 +1207,7 @@ "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Įvyko klaida atnaujinant ženkliukus šiam kambariui. Serveris gali jų neleisti arba įvyko laikina klaida.", "Showing flair for these communities:": "Ženkliukai rodomi šioms bendruomenėms:", "This room is not showing flair for any communities": "Šis kambarys nerodo ženkliukų jokioms bendruomenėms", - "Waiting for you to accept on your other session…": "Laukiama kol jūs priimsite savo kitame seanse…", + "Waiting for you to accept on your other session…": "Laukiama kol jūs priimsite kitame savo seanse…", "Start Verification": "Pradėti patvirtinimą", "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.": "Šifruotuose kambariuose jūsų žinutės yra apsaugotos ir tik jūs ir gavėjas turite unikalius raktus joms atrakinti.", "Verify User": "Patvirtinti vartotoją", @@ -1224,13 +1224,13 @@ "%(displayName)s cancelled verification.": "%(displayName)s atšaukė patvirtinimą.", "You cancelled verification.": "Jūs atšaukėte patvirtinimą.", "Verification cancelled": "Patvirtinimas atšauktas", - "Encryption enabled": "Šifravimas įgalintas", + "Encryption enabled": "Šifravimas įjungtas", "Encryption not enabled": "Šifravimas neįgalintas", "Display your community flair in rooms configured to show it.": "Rodyti savo bendruomenės ženkliukus kambariuose, kuriuose nustatytas jų rodymas.", "You're not currently a member of any communities.": "Jūs šiuo metu nesate jokios bendruomenės narys.", "More options": "Daugiau parinkčių", "Are you sure you want to remove %(serverName)s": "Ar tikrai norite pašalinti %(serverName)s", - "Enable end-to-end encryption": "Įgalinti visapusį šifravimą", + "Enable end-to-end encryption": "Įjungti visapusį šifravimą", "You can’t disable this later. Bridges & most bots won’t work yet.": "Jūs negalėsite vėliau to išjungti. Tiltai ir dauguma bot'ų dar nėra palaikomi.", "Are you sure you want to deactivate your account? This is irreversible.": "Ar tikrai norite deaktyvuoti savo paskyrą? Tai yra negrįžtama.", "Verify session": "Patvirtinti seansą", @@ -1240,7 +1240,7 @@ "Are you sure you want to sign out?": "Ar tikrai norite atsijungti?", "Use this session to verify your new one, granting it access to encrypted messages:": "Panaudoti šį seansą naujo patvirtinimui, suteikant jam prieigą prie šifruotų žinučių:", "If you didn’t sign in to this session, your account may be compromised.": "Jei jūs nesijungėte prie šios sesijos, jūsų paskyra gali būti sukompromituota.", - "This wasn't me": "Tai buvau ne aš", + "This wasn't me": "Tai ne aš", "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Jei jūs susidūrėte su klaidomis arba norėtumėte palikti atsiliepimą, praneškite mums GitHub'e.", "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.": "Tam, kad būtų išvengta pasikartojančių problemų, pirmiausia peržiūrėkite esamas problemas (ir pridėkite +1) arba, jei nerandate, sukurkite naują svarstomą problemą.", "Report bugs & give feedback": "Pranešti apie klaidas ir palikti atsiliepimą", @@ -1259,7 +1259,7 @@ "You can now close this window or log in to your new account.": "Jūs galite uždaryti šį langą arba prisijungti į savo naują paskyrą.", "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Patvirtinkite savo tapatybę verifikuodami šį prisijungimą viename iš kitų jūsų seansų, suteikdami jam prieigą prie šifruotų žinučių.", "This requires the latest Riot on your other devices:": "Tam reikia naujausios Riot versijos kituose jūsų įrenginiuose:", - "or another cross-signing capable Matrix client": "arba kitas kryžminį parašą palaikantis Matrix klientas", + "or another cross-signing capable Matrix client": "arba kitą kryžminį pasirašymą palaikantį Matrix klientą", "Use Recovery Passphrase or Key": "Naudoti atgavimo slaptafrazę arba raktą", "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Atnaujinkite šį seansą, kad jam būtų leista patvirtinti kitus seansus, suteikiant jiems prieigą prie šifruotų žinučių ir juos pažymint kaip patikimus kitiems vartotojams.", "Use Single Sign On to continue": "Norėdami tęsti naudokite Vieno Prisijungimo sistemą", @@ -1308,12 +1308,163 @@ "Room Topic": "Kambario Tema", "Your theme": "Jūsų tema", "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Valdiklio ištrinimas pašalina jį visiems kambaryje esantiems vartotojams. Ar tikrai norite ištrinti šį valdiklį?", - "Enable 'Manage Integrations' in Settings to do this.": "Įgalinkite 'Valdyti integracijas' nustatymuose, kad tai atliktumėte.", + "Enable 'Manage Integrations' in Settings to do this.": "Įjunkite 'Valdyti integracijas' nustatymuose, kad tai atliktumėte.", "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Jūsų Riot neleidžia jums naudoti integracijų valdiklio tam atlikti. Susisiekite su administratoriumi.", "Enter phone number (required on this homeserver)": "Įveskite telefono numerį (privaloma šiame serveryje)", "Doesn't look like a valid phone number": "Tai nepanašu į veikiantį telefono numerį", "Invalid homeserver discovery response": "Klaidingas serverio radimo atsakas", "Invalid identity server discovery response": "Klaidingas tapatybės serverio radimo atsakas", "The phone number entered looks invalid": "Įvestas telefono numeris atrodo klaidingas", - "Double check that your server supports the room version chosen and try again.": "Patikrinkite ar jūsų serveris palaiko pasirinktą kambario versiją ir bandykite iš naujo." + "Double check that your server supports the room version chosen and try again.": "Dar kartą įsitikinkite, kad jūsų serveris palaiko pasirinktą kambario versiją ir bandykite iš naujo.", + "Whether you're using Riot on a device where touch is the primary input mechanism": "Nesvarbu, ar naudojate „Riot“ įrenginyje, kuriame pagrindinis įvesties mechanizmas yra lietimas", + "Session already verified!": "Seansas jau patvirtintas!", + "WARNING: Session already verified, but keys do NOT MATCH!": "ĮSPĖJIMAS: Seansas jau patvirtintas, bet raktai NESUTAMPA!", + "Enable cross-signing to verify per-user instead of per-session": "Įjunkite kryžminį pasirašymą, kad patvirtintumėte vartotoją, o ne seansą", + "Enable Emoji suggestions while typing": "Įjungti emoji pasiūlymus rašant", + "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Rodyti priminimą įjungti saugų žinučių atgavimą šifruotuose kambariuose", + "Enable automatic language detection for syntax highlighting": "Įjungti automatinį kalbos aptikimą sintaksės paryškinimui", + "Enable big emoji in chat": "Įjungti didelius emoji pokalbiuose", + "Enable Community Filter Panel": "Įjungti bendruomenės filtrų skydelį", + "Enable message search in encrypted rooms": "Įjungti žinučių paiešką užšifruotuose kambariuose", + "Verified!": "Patvirtinta!", + "You've successfully verified this user.": "Jūs sėkmingai patvirtinote šį vartotoją.", + "Got It": "Supratau", + "Verify this session by completing one of the following:": "Patvirtinkite šį seansą atlikdami vieną iš šių veiksmų:", + "Scan this unique code": "Nuskaitykite šį unikalų kodą", + "Compare unique emoji": "Palyginkite unikalius emoji", + "Compare a unique set of emoji if you don't have a camera on either device": "Palyginkite unikalų emoji rinkinį, jei neturite fotoaparato nei viename įrenginyje", + "Confirm the emoji below are displayed on both sessions, in the same order:": "Patvirtinkite, kad žemiau esantys emoji yra rodomi abiejuose seansuose, ta pačia tvarka:", + "Waiting for %(displayName)s to verify…": "Laukiama kol %(displayName)s patvirtins…", + "Cancelling…": "Atšaukiama…", + "They match": "Jie sutampa", + "They don't match": "Jie nesutampa", + "Dog": "Šuo", + "Cat": "Katė", + "Lion": "Liūtas", + "Horse": "Arklys", + "Unicorn": "Vienaragis", + "Pig": "Kiaulė", + "Elephant": "Dramblys", + "Rabbit": "Triušis", + "Panda": "Panda", + "Rooster": "Gaidys", + "Penguin": "Pingvinas", + "Turtle": "Vėžlys", + "Fish": "Žuvis", + "Octopus": "Aštunkojis", + "Butterfly": "Drugelis", + "Flower": "Gėlė", + "Tree": "Medis", + "Cactus": "Kaktusas", + "Mushroom": "Grybas", + "Globe": "Gaublys", + "Moon": "Mėnulis", + "Cloud": "Debesis", + "Fire": "Ugnis", + "Banana": "Bananas", + "Apple": "Obuolys", + "Strawberry": "Braškė", + "Corn": "Kukurūzas", + "Pizza": "Pica", + "Cake": "Tortas", + "Heart": "Širdis", + "Smiley": "Šypsenėlė", + "Robot": "Robotas", + "Hat": "Skrybėlė", + "Glasses": "Akiniai", + "Spanner": "Veržliaraktis", + "Santa": "Santa", + "Thumbs up": "Liuks", + "Umbrella": "Skėtis", + "Hourglass": "Smėlio laikrodis", + "Clock": "Laikrodis", + "Gift": "Dovana", + "Light bulb": "Lemputė", + "Book": "Knyga", + "Pencil": "Pieštukas", + "Paperclip": "Sąvaržėlė", + "Scissors": "Žirklės", + "Lock": "Spyna", + "Key": "Raktas", + "Hammer": "Plaktukas", + "Telephone": "Telefonas", + "Flag": "Vėliava", + "Train": "Traukinys", + "Bicycle": "Dviratis", + "Aeroplane": "Lėktuvas", + "Rocket": "Raketa", + "Trophy": "Trofėjus", + "Ball": "Kamuolys", + "Guitar": "Gitara", + "Trumpet": "Trimitas", + "Bell": "Varpas", + "Anchor": "Inkaras", + "Headphones": "Ausinės", + "Folder": "Aplankas", + "Pin": "Smeigtukas", + "Other users may not trust it": "Kiti vartotojai gali nepasitikėti", + "Upgrade": "Atnaujinti", + "From %(deviceName)s (%(deviceId)s)": "Iš %(deviceName)s (%(deviceId)s)", + "Decline (%(counter)s)": "Atsisakyti (%(counter)s)", + "Accept to continue:": "Sutikite su , kad tęstumėte:", + "Upload": "Įkelti", + "Your homeserver does not support cross-signing.": "Jūsų serveris nepalaiko kryžminio pasirašymo.", + "Cross-signing and secret storage are enabled.": "Kryžminis pasirašymas ir slapta saugykla yra įjungta.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Jūsų paskyra slaptoje saugykloje turi kryžminio pasirašymo tapatybę, bet šis seansas dar ja nepasitiki.", + "Cross-signing and secret storage are not yet set up.": "Kryžminis pasirašymas ir slapta saugykla dar nėra nustatyti.", + "Reset cross-signing and secret storage": "Atstatyti kryžminį pasirašymą ir slaptą saugyklą", + "Bootstrap cross-signing and secret storage": "Prikabinti kryžminį pasirašymą ir slaptą saugyklą", + "Cross-signing public keys:": "Kryžminio pasirašymo vieši raktai:", + "Cross-signing private keys:": "Kryžminio pasirašymo privatūs raktai:", + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individualiai patikrinkite kiekvieną vartotojo naudojamą seansą, kad pažymėtumėte jį kaip patikimą, nepasitikint kryžminiu pasirašymu patvirtintais įrenginiais.", + "Enable": "Įjungti", + "Backup has a valid signature from verified session ": "Atsarginė kopija turi galiojantį parašą iš patikrinto seanso ", + "Backup has a valid signature from unverified session ": "Atsarginė kopija turi galiojantį parašą iš nepatikrinto seanso ", + "Backup has an invalid signature from verified session ": "Atsarginė kopija turi negaliojantį parašą iš patikrinto seanso ", + "Backup has an invalid signature from unverified session ": "Atsarginė kopija turi negaliojantį parašą iš nepatikrinto seanso ", + "Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Atsarginė rakto kopija saugoma slaptoje saugykloje, bet ši funkcija nėra įjungta šiame seanse. Įjunkite kryžminį pasirašymą Laboratorijose, kad galėtumėte keisti atsarginės rakto kopijos būseną.", + "Enable desktop notifications for this session": "Įjungti darbalaukio pranešimus šiam seansui", + "Enable audible notifications for this session": "Įjungti garsinius pranešimus šiam seansui", + "wait and try again later": "palaukite ir bandykite vėliau dar kartą", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Jei jūs nenorite naudoti radimui ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, žemiau įveskite kitą tapatybės serverį.", + "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Tapatybės serverio naudojimas yra pasirinktinis. Jei jūs pasirinksite jo nenaudoti, jūs nebūsite randamas kitų vartotojų ir neturėsite galimybės pakviesti kitų nurodydamas el. paštą ar telefoną.", + "Do not use an identity server": "Nenaudoti tapatybės serverio", + "Cross-signing": "Kryžminis pasirašymas", + "Error changing power level requirement": "Klaida keičiant galios lygio reikalavimą", + "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Keičiant kambario galios lygio reikalavimus įvyko klaida. Įsitikinkite, kad turite tam leidimą ir bandykite dar kartą.", + "Error changing power level": "Klaida keičiant galios lygį", + "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Keičiant vartotojo galios lygį įvyko klaida. Įsitikinkite, kad turite tam leidimą ir bandykite dar kartą.", + "Default role": "Numatytoji rolė", + "This user has not verified all of their sessions.": "Šis vartotojas nepatvirtino visų savo seansų.", + "You have not verified this user.": "Jūs nepatvirtinote šio vartotojo.", + "You have verified this user. This user has verified all of their sessions.": "Jūs patvirtinote šį vartotoją. Šis vartotojas patvirtino visus savo seansus.", + "Everyone in this room is verified": "Visi šiame kambaryje yra patvirtinti", + "Encrypted by a deleted session": "Užšifruota ištrintos sesijos", + "Use an identity server in Settings to receive invites directly in Riot.": "Nustatymuose naudokite tapatybės serverį, kad gautumėte pakvietimus tiesiai į Riot.", + "%(count)s verified sessions|one": "1 patvirtintas seansas", + "If you can't scan the code above, verify by comparing unique emoji.": "Jei nuskaityti aukščiau esančio kodo negalite, patvirtinkite palygindami unikalius emoji.", + "You've successfully verified your device!": "Jūs sėkmingai patvirtinote savo įrenginį!", + "You've successfully verified %(deviceName)s (%(deviceId)s)!": "Jūs sėkmingai patvirtinote %(deviceName)s (%(deviceId)s)!", + "You've successfully verified %(displayName)s!": "Jūs sėkmingai patvirtinote %(displayName)s!", + "Verified": "Patvirtinta", + "Got it": "Supratau", + "Start verification again from the notification.": "Pradėkite patvirtinimą iš naujo pranešime.", + "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Norėdami pakviesti nurodydami el. paštą, naudokite tapatybės serverį. Naudokite numatytajį (%(defaultIdentityServerName)s) arba tvarkykite Nustatymuose.", + "Use an identity server to invite by email. Manage in Settings.": "Norėdami pakviesti nurodydami el. paštą, naudokite tapatybės serverį. Tvarkykite Nustatymuose.", + "Destroy cross-signing keys?": "Sunaikinti kryžminio pasirašymo raktus?", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Kryžminio pasirašymo raktų ištrinimas yra neatšaukiamas. Visi, kurie buvo jais patvirtinti, matys saugumo įspėjimus. Jūs greičiausiai nenorite to daryti, nebent praradote visus įrenginius, iš kurių galite patvirtinti kryžminiu pasirašymu.", + "Clear cross-signing keys": "Valyti kryžminio pasirašymo raktus", + "To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Tam, kad patvirtintumėte šio seanso patikimumą, patikrinkite ar raktas, kurį matote Vartotojo Nustatymuose tame įrenginyje, sutampa su raktu esančiu žemiau:", + "Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Patvirtinkite šį įrenginį, kad pažymėtumėte jį kaip patikimą. Pasitikėjimas šiuo įrenginiu suteikia jums ir kitiems vartotojams papildomos ramybės, kai naudojate visapusiškai užšifruotas žinutes.", + "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Šio įrenginio patvirtinimas pažymės jį kaip patikimą ir vartotojai, kurie patvirtino su jumis, pasitikės šiuo įrenginiu.", + "a new cross-signing key signature": "naujas kryžminio pasirašymo rakto parašas", + "a device cross-signing signature": "įrenginio kryžminio pasirašymo parašas", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Pasiekite savo saugių žinučių istoriją ir kryžminio pasirašymo tapatybę, naudojamą kitų seansų patvirtinimui, įvesdami savo atgavimo raktą.", + "Session verified": "Seansas patvirtintas", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Neįmanoma prisijungti prie serverio per HTTP, kai naršyklės juostoje yra HTTPS URL. Naudokite HTTPS arba įjunkite nesaugias rašmenas.", + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Jūsų naujas seansas dabar yra patvirtintas. Jis turi prieigą prie jūsų šifruotų žinučių ir kiti vartotojai matys jį kaip patikimą.", + "Your new session is now verified. Other users will see it as trusted.": "Jūsų naujas seansas dabar yra patvirtintas. Kiti vartotojai matys jį kaip patikimą.", + "NOT verified": "Nepatvirtinta", + "verified": "patvirtinta", + "If you don't want to set this up now, you can later in Settings.": "Jei jūs dabar nenorite to nustatyti, galite padaryti tai vėliau Nustatymuose." } From cec8fa3595468d699994bbcac9d64c7cb18894ae Mon Sep 17 00:00:00 2001 From: strix aluco Date: Sat, 23 May 2020 16:58:41 +0000 Subject: [PATCH 304/399] Translated using Weblate (Ukrainian) Currently translated at 27.9% (647 of 2322 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index bdef596b88..fb6657d6c1 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -77,7 +77,7 @@ "Email": "е-пошта", "Email address": "Адреса е-пошти", "Failed to send email": "Помилка відправки е-почти", - "Edit": "Редактувати", + "Edit": "Редагувати", "Unpin Message": "Відкріпити повідомлення", "Register": "Зареєструватися", "Rooms": "Кімнати", From 457f4c82db9de47ce3b4d6449e6794e6385b1cdc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 25 May 2020 14:59:03 +0100 Subject: [PATCH 305/399] Make some ARIA promises Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/emojipicker/Category.js | 8 +++++- src/components/views/emojipicker/Emoji.js | 11 +++++--- src/components/views/emojipicker/Header.js | 25 +++++++++++++------ .../views/emojipicker/QuickReactions.js | 2 +- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/components/views/emojipicker/Category.js b/src/components/views/emojipicker/Category.js index 3c4352105e..eb3f83dcdf 100644 --- a/src/components/views/emojipicker/Category.js +++ b/src/components/views/emojipicker/Category.js @@ -67,7 +67,13 @@ class Category extends React.PureComponent { const localScrollTop = Math.max(0, scrollTop - listTop); return ( -
    +

    {name}

    diff --git a/src/components/views/emojipicker/Emoji.js b/src/components/views/emojipicker/Emoji.js index 75f23c5761..36aa4ff782 100644 --- a/src/components/views/emojipicker/Emoji.js +++ b/src/components/views/emojipicker/Emoji.js @@ -16,6 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import {MenuItem} from "../../structures/ContextMenu"; class Emoji extends React.PureComponent { static propTypes = { @@ -30,14 +31,18 @@ class Emoji extends React.PureComponent { const { onClick, onMouseEnter, onMouseLeave, emoji, selectedEmojis } = this.props; const isSelected = selectedEmojis && selectedEmojis.has(emoji.unicode); return ( -
  1. onClick(emoji)} + onClick(emoji)} onMouseEnter={() => onMouseEnter(emoji)} onMouseLeave={() => onMouseLeave(emoji)} - className="mx_EmojiPicker_item_wrapper"> + className="mx_EmojiPicker_item_wrapper" + label={emoji.unicode} + >
    {emoji.unicode}
    -
  2. + ); } } diff --git a/src/components/views/emojipicker/Header.js b/src/components/views/emojipicker/Header.js index b98e90e9b1..b3951c9ea5 100644 --- a/src/components/views/emojipicker/Header.js +++ b/src/components/views/emojipicker/Header.js @@ -16,6 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import classNames from "classnames"; class Header extends React.PureComponent { static propTypes = { @@ -26,13 +27,23 @@ class Header extends React.PureComponent { render() { return ( - ); } diff --git a/src/components/views/emojipicker/QuickReactions.js b/src/components/views/emojipicker/QuickReactions.js index 0bc799d356..8a20a4659b 100644 --- a/src/components/views/emojipicker/QuickReactions.js +++ b/src/components/views/emojipicker/QuickReactions.js @@ -72,7 +72,7 @@ class QuickReactions extends React.Component { } -
      +
        {QUICK_REACTIONS.map(emoji => Date: Mon, 25 May 2020 15:46:44 +0100 Subject: [PATCH 306/399] Implement Tabs with Automatic Activation pattern for EmojiPicker header Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/emojipicker/EmojiPicker.js | 4 ++ src/components/views/emojipicker/Header.js | 59 ++++++++++++++++++- src/i18n/strings/en_EN.json | 1 + 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/components/views/emojipicker/EmojiPicker.js b/src/components/views/emojipicker/EmojiPicker.js index cacc15a5f9..16a0fc67e7 100644 --- a/src/components/views/emojipicker/EmojiPicker.js +++ b/src/components/views/emojipicker/EmojiPicker.js @@ -147,8 +147,12 @@ class EmojiPicker extends React.Component { // We update this here instead of through React to avoid re-render on scroll. if (cat.visible) { cat.ref.current.classList.add("mx_EmojiPicker_anchor_visible"); + cat.ref.current.setAttribute("aria-selected", true); + cat.ref.current.setAttribute("tabindex", 0); } else { cat.ref.current.classList.remove("mx_EmojiPicker_anchor_visible"); + cat.ref.current.setAttribute("aria-selected", false); + cat.ref.current.setAttribute("tabindex", -1); } } } diff --git a/src/components/views/emojipicker/Header.js b/src/components/views/emojipicker/Header.js index b3951c9ea5..c53437e02d 100644 --- a/src/components/views/emojipicker/Header.js +++ b/src/components/views/emojipicker/Header.js @@ -18,20 +18,74 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; +import {_t} from "../../../languageHandler"; +import {Key} from "../../../Keyboard"; + class Header extends React.PureComponent { static propTypes = { categories: PropTypes.arrayOf(PropTypes.object).isRequired, onAnchorClick: PropTypes.func.isRequired, - refs: PropTypes.object, + }; + + findNearestEnabled(index, delta) { + index += this.props.categories.length; + const cats = [...this.props.categories, ...this.props.categories, ...this.props.categories]; + + while (index < cats.length && index >= 0) { + if (cats[index].enabled) return index % this.props.categories.length; + index += delta > 0 ? 1 : -1; + } + } + + changeCategoryRelative(delta) { + const current = this.props.categories.findIndex(c => c.visible); + this.changeCategoryAbsolute(current + delta, delta); + } + + changeCategoryAbsolute(index, delta=1) { + const category = this.props.categories[this.findNearestEnabled(index, delta)]; + if (category) { + this.props.onAnchorClick(category.id); + category.ref.current.focus(); + } + } + + // Implements ARIA Tabs with Automatic Activation pattern + // https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html + onKeyDown = (ev) => { + let handled = true; + switch (ev.key) { + case Key.ARROW_LEFT: + this.changeCategoryRelative(-1); + break; + case Key.ARROW_RIGHT: + this.changeCategoryRelative(1); + break; + + case Key.HOME: + this.changeCategoryAbsolute(0); + break; + case Key.END: + this.changeCategoryAbsolute(this.props.categories.length - 1, -1); + break; + default: + handled = false; + } + + if (handled) { + ev.preventDefault(); + ev.stopPropagation(); + } }; render() { return ( -
    -
    - ); - }, -}); diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index 2c835e6967..59e3d4c230 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -116,11 +116,6 @@ export default createReactClass({ this.closeMenu(); }, - e2eInfoClicked: function() { - this.props.e2eInfoCallback(); - this.closeMenu(); - }, - onReportEventClick: function() { const ReportEventDialog = sdk.getComponent("dialogs.ReportEventDialog"); Modal.createTrackedDialog('Report Event', '', ReportEventDialog, { @@ -465,15 +460,6 @@ export default createReactClass({ ); } - let e2eInfo; - if (this.props.e2eInfoCallback) { - e2eInfo = ( - - { _t('End-to-end encryption information') } - - ); - } - let reportEventButton; if (mxEvent.getSender() !== me) { reportEventButton = ( @@ -500,7 +486,6 @@ export default createReactClass({ { quoteButton } { externalURLButton } { collapseReplyThread } - { e2eInfo } { reportEventButton }
    ); diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 48b9c58cb8..3021b1a434 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -1,7 +1,7 @@ /* Copyright 2019 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -41,18 +41,6 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo const tile = getTile && getTile(); const replyThread = getReplyThread && getReplyThread(); - const onCryptoClick = () => { - Modal.createTrackedDialogAsync('Encrypted Event Dialog', '', - import('../../../async-components/views/dialogs/EncryptedEventDialog'), - {event: mxEvent}, - ); - }; - - let e2eInfoCallback = null; - if (mxEvent.isEncrypted() && !SettingsStore.getValue("feature_cross_signing")) { - e2eInfoCallback = onCryptoClick; - } - const buttonRect = button.current.getBoundingClientRect(); contextMenu = ; From 352727906e708b17491cff1efe0c05c748bcf657 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 May 2020 13:40:36 +0100 Subject: [PATCH 332/399] i18n --- src/i18n/strings/en_EN.json | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 53a4aa58f1..c3ce05b932 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1856,7 +1856,6 @@ "Share Message": "Share Message", "Source URL": "Source URL", "Collapse Reply Thread": "Collapse Reply Thread", - "End-to-end encryption information": "End-to-end encryption information", "Report Content": "Report Content", "Failed to set Direct Message status of room": "Failed to set Direct Message status of room", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", @@ -2176,22 +2175,6 @@ "Room Autocomplete": "Room Autocomplete", "Users": "Users", "User Autocomplete": "User Autocomplete", - "unknown device": "unknown device", - "NOT verified": "NOT verified", - "Blacklisted": "Blacklisted", - "verified": "verified", - "Device ID": "Device ID", - "Verification": "Verification", - "Ed25519 fingerprint": "Ed25519 fingerprint", - "User ID": "User ID", - "Curve25519 identity key": "Curve25519 identity key", - "none": "none", - "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key", - "Algorithm": "Algorithm", - "unencrypted": "unencrypted", - "Decryption error": "Decryption error", - "Event information": "Event information", - "Sender session information": "Sender session information", "Passphrases must match": "Passphrases must match", "Passphrase must not be empty": "Passphrase must not be empty", "Export room keys": "Export room keys", From 563a36e0a1f758f79f00af697a928cda2fc18866 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 May 2020 13:42:00 +0100 Subject: [PATCH 333/399] Remove unused CSS too --- res/css/_components.scss | 1 - .../views/dialogs/_EncryptedEventDialog.scss | 34 ------------------- 2 files changed, 35 deletions(-) delete mode 100644 res/css/views/dialogs/_EncryptedEventDialog.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 3a6a3257a3..44c63b9df7 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -63,7 +63,6 @@ @import "./views/dialogs/_DeactivateAccountDialog.scss"; @import "./views/dialogs/_DeviceVerifyDialog.scss"; @import "./views/dialogs/_DevtoolsDialog.scss"; -@import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_InviteDialog.scss"; diff --git a/res/css/views/dialogs/_EncryptedEventDialog.scss b/res/css/views/dialogs/_EncryptedEventDialog.scss deleted file mode 100644 index ff73df509d..0000000000 --- a/res/css/views/dialogs/_EncryptedEventDialog.scss +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_EncryptedEventDialog .mx_DeviceVerifyButtons { - float: right; - padding: 0px; - margin-right: 42px; - display: flex; - flex-wrap: wrap; - justify-content: space-between; -} - -.mx_EncryptedEventDialog .mx_MemberDeviceInfo_textButton { - @mixin mx_DialogButton; - background-color: $primary-bg-color; - color: $accent-color; -} - -.mx_EncryptedEventDialog button { - margin-top: 0px; -} From d2fe4bcecdcb07258085a6265a4df890e17f73bc Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 May 2020 13:46:47 +0100 Subject: [PATCH 334/399] Unused imports --- src/components/views/messages/MessageActionBar.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 3021b1a434..95eb37b588 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -22,11 +22,9 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; -import Modal from '../../../Modal'; import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu'; import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; import RoomContext from "../../../contexts/RoomContext"; -import SettingsStore from '../../../settings/SettingsStore'; const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange}) => { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); From fb73bac4cf7c847d088be2f02414c82c4eeee041 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 26 May 2020 13:53:28 +0100 Subject: [PATCH 335/399] Update UpdateToast's priority to 30 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/UpdateToast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index 3d4b55a4ff..55c128e86a 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -81,7 +81,7 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st onAccept, }, component: GenericToast, - priority: 20, + priority: 30, }); }; From f1282179fb2aa880b2dccfafef7c4a72abae9ed6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 26 May 2020 14:58:09 +0200 Subject: [PATCH 336/399] add logging when we fail to join a room --- src/stores/RoomViewStore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 3d82d086d7..7fa1ef639f 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -272,6 +272,7 @@ class RoomViewStore extends Store { err: err, }); let msg = err.message ? err.message : JSON.stringify(err); + console.log("Failed to join room:", msg); // XXX: We are relying on the error message returned by browsers here. // This isn't great, but it does generalize the error being shown to users. if (msg && msg.startsWith("CORS request rejected")) { From cfe517323512273682702bfbb87357501a4de4d3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 26 May 2020 15:20:58 +0200 Subject: [PATCH 337/399] use connection error to detect network problem --- src/stores/RoomViewStore.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 7fa1ef639f..612d786468 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -273,9 +273,7 @@ class RoomViewStore extends Store { }); let msg = err.message ? err.message : JSON.stringify(err); console.log("Failed to join room:", msg); - // XXX: We are relying on the error message returned by browsers here. - // This isn't great, but it does generalize the error being shown to users. - if (msg && msg.startsWith("CORS request rejected")) { + if (err.name === "ConnectionError") { msg = _t("There was an error joining the room"); } if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { From fcbd197e7df2c7f32b98c3cf4c704f4d2f90e979 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 26 May 2020 15:56:16 +0100 Subject: [PATCH 338/399] Fix file list regression --- res/css/structures/_NotificationPanel.scss | 4 ++++ src/components/structures/MessagePanel.js | 22 ++++------------------ src/components/structures/RoomView.js | 21 ++++++++++++++++++++- src/components/structures/TimelinePanel.js | 4 ++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index 44205b1f01..561ab1446f 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -63,6 +63,10 @@ limitations under the License. padding-left: 32px; padding-top: 8px; position: relative; + + a { + display: flex; + } } .mx_NotificationPanel .mx_EventTile_roomName a, diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 93e4668f66..ef738db2fd 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -108,6 +108,9 @@ export default class MessagePanel extends React.Component { // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }; // Force props to be loaded for useIRCLayout @@ -119,7 +122,6 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -172,8 +174,6 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); - - this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); } componentDidMount() { @@ -183,7 +183,6 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); - SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -202,17 +201,6 @@ export default class MessagePanel extends React.Component { }); }; - onLayoutChange = () => { - this.setState({ - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), - }); - } - - useIRCLayout(ircLayoutSelected) { - // if room is null we are not in a normal room list - return ircLayoutSelected && this.props.room; - } - /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -614,7 +602,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} - useIRCLayout={this.state.useIRCLayout} + useIRCLayout={this.props.useIRCLayout} /> , @@ -797,8 +785,6 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, - "mx_IRCLayout": this.state.useIRCLayout, - "mx_GroupLayout": !this.state.useIRCLayout, }, ); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 635597db74..b9daac17b4 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -164,6 +164,7 @@ export default createReactClass({ canReact: false, canReply: false, + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), }; }, @@ -193,6 +194,8 @@ export default createReactClass({ this._roomView = createRef(); this._searchResultsPanel = createRef(); + + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); }, _onReadReceiptsChange: function() { @@ -532,6 +535,14 @@ export default createReactClass({ // no need to do this as Dir & Settings are now overlays. It just burnt CPU. // console.log("Tinter.tint from RoomView.unmount"); // Tinter.tint(); // reset colourscheme + + SettingsStore.unwatchSetting(this._layoutWatcherRef); + }, + + onLayoutChange: function() { + this.setState({ + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + }); }, _onRightPanelStoreUpdate: function() { @@ -1980,6 +1991,13 @@ export default createReactClass({ highlightedEventId = this.state.initialEventId; } + const messagePanelClassNames = classNames( + "mx_RoomView_messagePanel", + { + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, + }); + // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); const messagePanel = ( ); let topUnreadMessagesBar = null; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index da1369c45f..95dc42fcee 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -112,6 +112,9 @@ const TimelinePanel = createReactClass({ // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }, statics: { @@ -1447,6 +1450,7 @@ const TimelinePanel = createReactClass({ getRelationsForEvent={this.getRelationsForEvent} editState={this.state.editState} showReactions={this.props.showReactions} + useIRCLayout={this.props.useIRCLayout} /> ); }, From a496dafd417cdc8b784bab0eedd7faba74da8d6c Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 May 2020 16:51:20 +0100 Subject: [PATCH 339/399] Remove the keyshare dialog Fixes https://github.com/vector-im/riot-web/issues/11908 --- src/KeyRequestHandler.js | 158 ---------------- src/components/structures/MatrixChat.tsx | 11 -- .../views/dialogs/KeyShareDialog.js | 178 ------------------ src/i18n/strings/en_EN.json | 7 - 4 files changed, 354 deletions(-) delete mode 100644 src/KeyRequestHandler.js delete mode 100644 src/components/views/dialogs/KeyShareDialog.js diff --git a/src/KeyRequestHandler.js b/src/KeyRequestHandler.js deleted file mode 100644 index ceaff0c54d..0000000000 --- a/src/KeyRequestHandler.js +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright 2017 Vector Creations Ltd -Copyright 2020 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 * as sdk from './index'; -import Modal from './Modal'; -import SettingsStore from './settings/SettingsStore'; - -// TODO: We can remove this once cross-signing is the only way. -// https://github.com/vector-im/riot-web/issues/11908 -export default class KeyRequestHandler { - constructor(matrixClient) { - this._matrixClient = matrixClient; - - // the user/device for which we currently have a dialog open - this._currentUser = null; - this._currentDevice = null; - - // userId -> deviceId -> [keyRequest] - this._pendingKeyRequests = Object.create(null); - } - - handleKeyRequest(keyRequest) { - // Ignore own device key requests if cross-signing lab enabled - if (SettingsStore.getValue("feature_cross_signing")) { - return; - } - - const userId = keyRequest.userId; - const deviceId = keyRequest.deviceId; - const requestId = keyRequest.requestId; - - if (!this._pendingKeyRequests[userId]) { - this._pendingKeyRequests[userId] = Object.create(null); - } - if (!this._pendingKeyRequests[userId][deviceId]) { - this._pendingKeyRequests[userId][deviceId] = []; - } - - // check if we already have this request - const requests = this._pendingKeyRequests[userId][deviceId]; - if (requests.find((r) => r.requestId === requestId)) { - console.log("Already have this key request, ignoring"); - return; - } - - requests.push(keyRequest); - - if (this._currentUser) { - // ignore for now - console.log("Key request, but we already have a dialog open"); - return; - } - - this._processNextRequest(); - } - - handleKeyRequestCancellation(cancellation) { - // Ignore own device key requests if cross-signing lab enabled - if (SettingsStore.getValue("feature_cross_signing")) { - return; - } - - // see if we can find the request in the queue - const userId = cancellation.userId; - const deviceId = cancellation.deviceId; - const requestId = cancellation.requestId; - - if (userId === this._currentUser && deviceId === this._currentDevice) { - console.log( - "room key request cancellation for the user we currently have a" - + " dialog open for", - ); - // TODO: update the dialog. For now, we just ignore the - // cancellation. - return; - } - - if (!this._pendingKeyRequests[userId]) { - return; - } - const requests = this._pendingKeyRequests[userId][deviceId]; - if (!requests) { - return; - } - const idx = requests.findIndex((r) => r.requestId === requestId); - if (idx < 0) { - return; - } - console.log("Forgetting room key request"); - requests.splice(idx, 1); - if (requests.length === 0) { - delete this._pendingKeyRequests[userId][deviceId]; - if (Object.keys(this._pendingKeyRequests[userId]).length === 0) { - delete this._pendingKeyRequests[userId]; - } - } - } - - _processNextRequest() { - const userId = Object.keys(this._pendingKeyRequests)[0]; - if (!userId) { - return; - } - const deviceId = Object.keys(this._pendingKeyRequests[userId])[0]; - if (!deviceId) { - return; - } - console.log(`Starting KeyShareDialog for ${userId}:${deviceId}`); - - const finished = (r) => { - this._currentUser = null; - this._currentDevice = null; - - if (!this._pendingKeyRequests[userId] || !this._pendingKeyRequests[userId][deviceId]) { - // request was removed in the time the dialog was displayed - this._processNextRequest(); - return; - } - - if (r) { - for (const req of this._pendingKeyRequests[userId][deviceId]) { - req.share(); - } - } - delete this._pendingKeyRequests[userId][deviceId]; - if (Object.keys(this._pendingKeyRequests[userId]).length === 0) { - delete this._pendingKeyRequests[userId]; - } - - this._processNextRequest(); - }; - - const KeyShareDialog = sdk.getComponent("dialogs.KeyShareDialog"); - Modal.appendTrackedDialog('Key Share', 'Process Next Request', KeyShareDialog, { - matrixClient: this._matrixClient, - userId: userId, - deviceId: deviceId, - onFinished: finished, - }); - this._currentUser = userId; - this._currentDevice = deviceId; - } -} - diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f1992bbfcf..1200fcaf7a 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -49,7 +49,6 @@ import PageTypes from '../../PageTypes'; import { getHomePageUrl } from '../../utils/pages'; import createRoom from "../../createRoom"; -import KeyRequestHandler from '../../KeyRequestHandler'; import { _t, getCurrentLanguage } from '../../languageHandler'; import SettingsStore, { SettingLevel } from "../../settings/SettingsStore"; import ThemeController from "../../settings/controllers/ThemeController"; @@ -1471,16 +1470,6 @@ export default class MatrixChat extends React.PureComponent { cli.on("Session.logged_out", () => dft.stop()); cli.on("Event.decrypted", (e, err) => dft.eventDecrypted(e, err)); - // TODO: We can remove this once cross-signing is the only way. - // https://github.com/vector-im/riot-web/issues/11908 - const krh = new KeyRequestHandler(cli); - cli.on("crypto.roomKeyRequest", (req) => { - krh.handleKeyRequest(req); - }); - cli.on("crypto.roomKeyRequestCancellation", (req) => { - krh.handleKeyRequestCancellation(req); - }); - cli.on("Room", (room) => { if (MatrixClientPeg.get().isCryptoEnabled()) { const blacklistEnabled = SettingsStore.getValueAt( diff --git a/src/components/views/dialogs/KeyShareDialog.js b/src/components/views/dialogs/KeyShareDialog.js deleted file mode 100644 index 8ef36bb59f..0000000000 --- a/src/components/views/dialogs/KeyShareDialog.js +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2017 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. -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 Modal from '../../../Modal'; -import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; -import * as sdk from '../../../index'; - -import { _t, _td } from '../../../languageHandler'; - -// TODO: We can remove this once cross-signing is the only way. -// https://github.com/vector-im/riot-web/issues/11908 - -/** - * Dialog which asks the user whether they want to share their keys with - * an unverified device. - * - * onFinished is called with `true` if the key should be shared, `false` if it - * should not, and `undefined` if the dialog is cancelled. (In other words: - * truthy: do the key share. falsy: don't share the keys). - */ -export default createReactClass({ - propTypes: { - matrixClient: PropTypes.object.isRequired, - userId: PropTypes.string.isRequired, - deviceId: PropTypes.string.isRequired, - onFinished: PropTypes.func.isRequired, - }, - - getInitialState: function() { - return { - deviceInfo: null, - wasNewDevice: false, - }; - }, - - componentDidMount: function() { - this._unmounted = false; - const userId = this.props.userId; - const deviceId = this.props.deviceId; - - // give the client a chance to refresh the device list - this.props.matrixClient.downloadKeys([userId], false).then((r) => { - if (this._unmounted) { return; } - - const deviceInfo = r[userId][deviceId]; - - if (!deviceInfo) { - console.warn(`No details found for session ${userId}:${deviceId}`); - - this.props.onFinished(false); - return; - } - - const wasNewDevice = !deviceInfo.isKnown(); - - this.setState({ - deviceInfo: deviceInfo, - wasNewDevice: wasNewDevice, - }); - - // if the device was new before, it's not any more. - if (wasNewDevice) { - this.props.matrixClient.setDeviceKnown( - userId, - deviceId, - true, - ); - } - }); - }, - - componentWillUnmount: function() { - this._unmounted = true; - }, - - - _onVerifyClicked: function() { - const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog'); - - console.log("KeyShareDialog: Starting verify dialog"); - Modal.createTrackedDialog('Key Share', 'Starting dialog', DeviceVerifyDialog, { - userId: this.props.userId, - device: this.state.deviceInfo, - onFinished: (verified) => { - if (verified) { - // can automatically share the keys now. - this.props.onFinished(true); - } - }, - }, null, /* priority = */ false, /* static = */ true); - }, - - _onShareClicked: function() { - console.log("KeyShareDialog: User clicked 'share'"); - this.props.onFinished(true); - }, - - _onIgnoreClicked: function() { - console.log("KeyShareDialog: User clicked 'ignore'"); - this.props.onFinished(false); - }, - - _renderContent: function() { - const displayName = this.state.deviceInfo.getDisplayName() || - this.state.deviceInfo.deviceId; - - let text; - if (this.state.wasNewDevice) { - text = _td("You added a new session '%(displayName)s', which is" - + " requesting encryption keys."); - } else { - text = _td("Your unverified session '%(displayName)s' is requesting" - + " encryption keys."); - } - text = _t(text, {displayName: displayName}); - - return ( -
    -

    { text }

    - -
    - - - -
    -
    - ); - }, - - render: function() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const Spinner = sdk.getComponent('views.elements.Spinner'); - - let content; - - if (this.state.deviceInfo) { - content = this._renderContent(); - } else { - content = ( -
    -

    { _t('Loading session info...') }

    - -
    - ); - } - - return ( - - { content } - - ); - }, -}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7474af2ee7..dc583d8d9a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1669,13 +1669,6 @@ "Start a conversation with someone using their name, username (like ) or email address.": "Start a conversation with someone using their name, username (like ) or email address.", "Go": "Go", "Invite someone using their name, username (like ), email address or share this room.": "Invite someone using their name, username (like ), email address or share this room.", - "You added a new session '%(displayName)s', which is requesting encryption keys.": "You added a new session '%(displayName)s', which is requesting encryption keys.", - "Your unverified session '%(displayName)s' is requesting encryption keys.": "Your unverified session '%(displayName)s' is requesting encryption keys.", - "Start verification": "Start verification", - "Share without verifying": "Share without verifying", - "Ignore request": "Ignore request", - "Loading session info...": "Loading session info...", - "Encryption key request": "Encryption key request", "a new master key signature": "a new master key signature", "a new cross-signing key signature": "a new cross-signing key signature", "a device cross-signing signature": "a device cross-signing signature", From d8836f0f4ba9707ac8690e58d64bd094a0eafb9e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 26 May 2020 17:09:42 +0100 Subject: [PATCH 340/399] Fir sublist sizing regression --- res/css/views/rooms/_RoomTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 759dce5afa..7f93da0bbf 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: 32px; + height: 34px; margin: 0; padding: 0 8px 0 10px; position: relative; From 792859b939ee0a0a1b92441a49c9898318ef70e3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 26 May 2020 17:39:39 +0100 Subject: [PATCH 341/399] Fix lines overflowing room list width --- res/css/views/rooms/_IRCLayout.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 5f88473c5f..821a5214ec 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -63,6 +63,8 @@ $irc-line-height: $font-18px; flex-direction: column; order: 3; flex-grow: 1; + flex-shrink: 1; + min-width: 0; } > .mx_EventTile_avatar { From 73213422a9997d7b5323695d8f725f921f20334a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 26 May 2020 18:15:06 +0100 Subject: [PATCH 342/399] fix encryption icon missalignment --- res/css/views/rooms/_IRCLayout.scss | 10 ++++++---- src/components/views/rooms/EventTile.js | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 5f88473c5f..cda55b0944 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -41,7 +41,7 @@ $irc-line-height: $font-18px; } > .mx_EventTile_msgOption { - order: 4; + order: 5; flex-shrink: 0; } @@ -90,12 +90,14 @@ $irc-line-height: $font-18px; text-align: right; } - .mx_EventTile_e2eIcon { + > .mx_EventTile_e2eIcon { position: relative; right: unset; left: unset; - top: -2px; padding: 0; + order: 3; + flex-shrink: 0; + flex-grow: 0; } .mx_EventTile_line { @@ -113,7 +115,7 @@ $irc-line-height: $font-18px; } .mx_EventTile_reply { - order: 3; + order: 4; } .mx_EditMessageComposer_buttons { diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index ee0b40c0de..a822875a18 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -802,6 +802,8 @@ export default createReactClass({ const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; + const groupPadlock = !this.props.useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); + const ircPadlock = this.props.useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); switch (this.props.tileShape) { case 'notif': { @@ -873,9 +875,10 @@ export default createReactClass({ { ircTimestamp } { avatar } { sender } + { ircPadlock }
    { groupTimestamp } - { !isBubbleMessage && this._renderE2EPadlock() } + { groupPadlock } { thread } { sender } + { ircPadlock }
    { groupTimestamp } - { !isBubbleMessage && this._renderE2EPadlock() } + { groupPadlock } { thread } Date: Tue, 26 May 2020 20:47:30 +0200 Subject: [PATCH 343/399] EventIndex: Print out the checkpoint if there was an error during a crawl. --- src/indexing/EventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index d372c38405..a02e378d8e 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -405,7 +405,7 @@ export default class EventIndex extends EventEmitter { continue; } - console.log("EventIndex: Error crawling events:", e); + console.log("EventIndex: Error crawling using checkpoint:", checkpoint, ",", e); this.crawlerCheckpoints.push(checkpoint); continue; } From 58f0814530b22b5a03f10e74ee18827ea60100a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 May 2020 21:27:45 +0200 Subject: [PATCH 344/399] EventIndex: Handle invalid m.room.redaction events correctly. Clients might send invalid redaction events, such events will not have a valid redacts field containing the event id of the associated event that was redacted. Skip such events instead of ending up looping over the checkpoint forever. --- src/indexing/EventIndex.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index a02e378d8e..b0dd811e0c 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -507,7 +507,14 @@ export default class EventIndex extends EventEmitter { try { for (let i = 0; i < redactionEvents.length; i++) { const ev = redactionEvents[i]; - await indexManager.deleteEvent(ev.getAssociatedId()); + const eventId = ev.getAssociatedId(); + + if (eventId) { + await indexManager.deleteEvent(eventId); + } else { + console.log("EventIndex: Redaction event doesn't contain a", + "valid associated event id", ev); + } } const eventsAlreadyAdded = await indexManager.addHistoricEvents( From 6c10cbc9032519a4a31373a84b5b3f8b8eb97459 Mon Sep 17 00:00:00 2001 From: poljar Date: Tue, 26 May 2020 21:48:10 +0200 Subject: [PATCH 345/399] EventIndex: Use a warning when redactions don't have an event id. Co-authored-by: Travis Ralston --- src/indexing/EventIndex.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index b0dd811e0c..fac7c92b65 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -512,8 +512,7 @@ export default class EventIndex extends EventEmitter { if (eventId) { await indexManager.deleteEvent(eventId); } else { - console.log("EventIndex: Redaction event doesn't contain a", - "valid associated event id", ev); + console.warn("EventIndex: Redaction event doesn't contain a valid associated event id", ev); } } From a9c54153eec696a86815ecb5a7047f30c72270f5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 25 May 2020 15:54:02 -0600 Subject: [PATCH 346/399] Update badge counts in new room list as needed --- src/components/views/rooms/RoomTile2.tsx | 21 +++++++++++++-- src/stores/room-list/RoomListStore2.ts | 14 ++++------ src/utils/read-receipts.ts | 34 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 src/utils/read-receipts.ts diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index ae1802de49..d9d1d39142 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -30,6 +30,8 @@ import * as RoomNotifs from '../../../RoomNotifs'; import { EffectiveMembership, getEffectiveMembership } from "../../../stores/room-list/membership"; import * as Unread from '../../../Unread'; import * as FormattingUtils from "../../../utils/FormattingUtils"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; /******************************************************************* * CAUTION * @@ -86,10 +88,19 @@ export default class RoomTile2 extends React.Component { hover: false, notificationState: this.getNotificationState(), }; + + // TODO: We shouldn't have to listen to every room update + // We don't have a model which works in a better way though. + MatrixClientPeg.get().on("Room.receipt", this.tryUpdateIfRoomMatches); + MatrixClientPeg.get().on("Room.timeline", this.tryUpdateIfRoomMatches); + MatrixClientPeg.get().on("Event.decrypted", this.tryUpdateIfRoomMatches); + MatrixClientPeg.get().on("Room.redaction", this.tryUpdateIfRoomMatches); } public componentWillUnmount() { - // TODO: Listen for changes to the badge count and update as needed + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("Room.receipt", this.tryUpdateIfRoomMatches); + } } // XXX: This is a bit of an awful-looking hack. We should probably be using state for @@ -99,7 +110,13 @@ export default class RoomTile2 extends React.Component { return getEffectiveMembership(this.props.room.getMyMembership()) === EffectiveMembership.Invite; } - // TODO: Make use of this function when the notification state needs updating. + private tryUpdateIfRoomMatches = (event: MatrixEvent) => { + const roomId = event.getRoomId(); + if (roomId !== this.props.room.roomId) return; + + this.updateNotificationState(); + }; + private updateNotificationState() { this.setState({notificationState: this.getNotificationState()}); } diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 881b8fd3cf..108922a598 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -26,6 +26,7 @@ import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorit import { getListAlgorithmInstance } from "./algorithms/list-ordering"; import { ActionPayload } from "../../dispatcher/payloads"; import defaultDispatcher from "../../dispatcher/dispatcher"; +import { readReceiptChangeIsFor } from "../../utils/read-receipts"; interface IState { tagsEnabled?: boolean; @@ -135,15 +136,10 @@ class _RoomListStore extends AsyncStore { if (payload.action === 'MatrixActions.Room.receipt') { // First see if the receipt event is for our own user. If it was, trigger // a room update (we probably read the room on a different device). - // noinspection JSObjectNullOrUndefined - this.matrixClient can't be null by this point in the lifecycle - const myUserId = this.matrixClient.getUserId(); - for (const eventId of Object.keys(payload.event.getContent())) { - const receiptUsers = Object.keys(payload.event.getContent()[eventId]['m.read'] || {}); - if (receiptUsers.includes(myUserId)) { - // TODO: Update room now that it's been read - console.log(payload); - return; - } + if (readReceiptChangeIsFor(payload.event, this.matrixClient)) { + // TODO: Update room now that it's been read + console.log(payload); + return; } } else if (payload.action === 'MatrixActions.Room.tags') { // TODO: Update room from tags diff --git a/src/utils/read-receipts.ts b/src/utils/read-receipts.ts new file mode 100644 index 0000000000..f05c3cc5f2 --- /dev/null +++ b/src/utils/read-receipts.ts @@ -0,0 +1,34 @@ +/* +Copyright 2020 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 { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { MatrixClient } from "matrix-js-sdk/src/client"; + +/** + * Determines if a read receipt update event includes the client's own user. + * @param event The event to check. + * @param client The client to check against. + * @returns True if the read receipt update includes the client, false otherwise. + */ +export function readReceiptChangeIsFor(event: MatrixEvent, client: MatrixClient): boolean { + const myUserId = client.getUserId(); + for (const eventId of Object.keys(event.getContent())) { + const receiptUsers = Object.keys(event.getContent()[eventId]['m.read'] || {}); + if (receiptUsers.includes(myUserId)) { + return true; + } + } +} From bf8a3d4419c66f0c5318d2a2d4f8b843e48ec3ea Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 26 May 2020 16:20:51 -0600 Subject: [PATCH 347/399] Bind to the room instead of the client Also add the other missing deregister handlers --- src/components/views/rooms/RoomTile2.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index d9d1d39142..47ce001983 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -89,17 +89,18 @@ export default class RoomTile2 extends React.Component { notificationState: this.getNotificationState(), }; - // TODO: We shouldn't have to listen to every room update - // We don't have a model which works in a better way though. - MatrixClientPeg.get().on("Room.receipt", this.tryUpdateIfRoomMatches); - MatrixClientPeg.get().on("Room.timeline", this.tryUpdateIfRoomMatches); - MatrixClientPeg.get().on("Event.decrypted", this.tryUpdateIfRoomMatches); - MatrixClientPeg.get().on("Room.redaction", this.tryUpdateIfRoomMatches); + this.props.room.on("Room.receipt", this.handleRoomEventUpdate); + this.props.room.on("Room.timeline", this.handleRoomEventUpdate); + this.props.room.on("Event.decrypted", this.handleRoomEventUpdate); + this.props.room.on("Room.redaction", this.handleRoomEventUpdate); } public componentWillUnmount() { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("Room.receipt", this.tryUpdateIfRoomMatches); + if (this.props.room) { + this.props.room.removeListener("Room.receipt", this.handleRoomEventUpdate); + this.props.room.removeListener("Room.timeline", this.handleRoomEventUpdate); + this.props.room.removeListener("Event.decrypted", this.handleRoomEventUpdate); + this.props.room.removeListener("Room.redaction", this.handleRoomEventUpdate); } } @@ -110,8 +111,10 @@ export default class RoomTile2 extends React.Component { return getEffectiveMembership(this.props.room.getMyMembership()) === EffectiveMembership.Invite; } - private tryUpdateIfRoomMatches = (event: MatrixEvent) => { + private handleRoomEventUpdate = (event: MatrixEvent) => { const roomId = event.getRoomId(); + + // Sanity check: should never happen if (roomId !== this.props.room.roomId) return; this.updateNotificationState(); From 8370fa8bb4485e7ac857fc045ebc09b3d864a3bb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 26 May 2020 19:56:56 -0600 Subject: [PATCH 348/399] Event.decrypted from the client --- src/components/views/rooms/RoomTile2.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 47ce001983..c95cd108dc 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -91,17 +91,19 @@ export default class RoomTile2 extends React.Component { this.props.room.on("Room.receipt", this.handleRoomEventUpdate); this.props.room.on("Room.timeline", this.handleRoomEventUpdate); - this.props.room.on("Event.decrypted", this.handleRoomEventUpdate); this.props.room.on("Room.redaction", this.handleRoomEventUpdate); + MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate); } public componentWillUnmount() { if (this.props.room) { this.props.room.removeListener("Room.receipt", this.handleRoomEventUpdate); this.props.room.removeListener("Room.timeline", this.handleRoomEventUpdate); - this.props.room.removeListener("Event.decrypted", this.handleRoomEventUpdate); this.props.room.removeListener("Room.redaction", this.handleRoomEventUpdate); } + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate); + } } // XXX: This is a bit of an awful-looking hack. We should probably be using state for From 254ed4b1b60c1bf8964a67febf2761a22773802b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 11:33:42 +0100 Subject: [PATCH 349/399] Add debug lined to figure out e2e test failure Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index e963ecd736..a26c5e0794 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -56,6 +56,7 @@ export default class ToastStore extends EventEmitter { * @param {object} newToast The new toast */ addOrReplaceToast>(newToast: IToast) { + console.log("DEBUG addOrReplaceToast", newToast.key, JSON.stringify(newToast)); const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; @@ -68,6 +69,7 @@ export default class ToastStore extends EventEmitter { } dismissToast(key) { + console.log("DEBUG dismissToast", key); const length = this.toasts.length; this.toasts = this.toasts.filter(t => t.key !== key); if (length !== this.toasts.length) { From 3dade4e759bc4d01af126e1f3a5d64c4057b1d20 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 11:48:48 +0100 Subject: [PATCH 350/399] tweak Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index a26c5e0794..2f5a1f74b0 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -70,13 +70,15 @@ export default class ToastStore extends EventEmitter { dismissToast(key) { console.log("DEBUG dismissToast", key); + if (this.toasts[0] && this.toasts[0].key === key) { + this.countSeen++; + } + const length = this.toasts.length; this.toasts = this.toasts.filter(t => t.key !== key); if (length !== this.toasts.length) { if (this.toasts.length === 0) { this.countSeen = 0; - } else { - this.countSeen++; } this.emit('update'); From efa47f1a8e948e2920aa640cccfad94caa9942b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:02:51 +0100 Subject: [PATCH 351/399] Change out debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 + src/stores/ToastStore.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f2d7e81d85..f8c96ee393 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -624,6 +624,7 @@ export default class MatrixChat extends React.PureComponent { break; } case 'view_room_directory': { + console.log("DEBUG view_room_directory"); const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, {}, 'mx_RoomDirectory_dialogWrapper', false, true); diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 2f5a1f74b0..55c48c3937 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -56,7 +56,6 @@ export default class ToastStore extends EventEmitter { * @param {object} newToast The new toast */ addOrReplaceToast>(newToast: IToast) { - console.log("DEBUG addOrReplaceToast", newToast.key, JSON.stringify(newToast)); const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; @@ -69,7 +68,6 @@ export default class ToastStore extends EventEmitter { } dismissToast(key) { - console.log("DEBUG dismissToast", key); if (this.toasts[0] && this.toasts[0].key === key) { this.countSeen++; } From 48de17457663867359b511344210d5d962200d29 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:20:20 +0100 Subject: [PATCH 352/399] remove debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f8c96ee393..f2d7e81d85 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -624,7 +624,6 @@ export default class MatrixChat extends React.PureComponent { break; } case 'view_room_directory': { - console.log("DEBUG view_room_directory"); const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, {}, 'mx_RoomDirectory_dialogWrapper', false, true); From b7c688d3282d5156c74f9ac50e0780908c895965 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:40:38 +0100 Subject: [PATCH 353/399] test e2e tests workaround Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/join.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/end-to-end-tests/src/usecases/join.js b/test/end-to-end-tests/src/usecases/join.js index 655c0be686..10a48bd71c 100644 --- a/test/end-to-end-tests/src/usecases/join.js +++ b/test/end-to-end-tests/src/usecases/join.js @@ -19,6 +19,15 @@ const {openRoomDirectory} = require('./create-room'); module.exports = async function join(session, roomName) { session.log.step(`joins room "${roomName}"`); + while (true) { + try { + const toastDismissButton = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + await openRoomDirectory(session); const roomInput = await session.query('.mx_DirectorySearchBox input'); await session.replaceInputText(roomInput, roomName); From 7b6d49c416f63576fcdf3be088b80cc2b87bde71 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:57:45 +0100 Subject: [PATCH 354/399] e2e test toasts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenario.js | 2 + test/end-to-end-tests/src/scenarios/toast.js | 23 ++++++++++ test/end-to-end-tests/src/usecases/join.js | 9 ---- test/end-to-end-tests/src/usecases/toasts.js | 45 ++++++++++++++++++++ 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/end-to-end-tests/src/scenarios/toast.js create mode 100644 test/end-to-end-tests/src/usecases/toasts.js diff --git a/test/end-to-end-tests/src/scenario.js b/test/end-to-end-tests/src/scenario.js index f575fb392e..2191d630ac 100644 --- a/test/end-to-end-tests/src/scenario.js +++ b/test/end-to-end-tests/src/scenario.js @@ -17,6 +17,7 @@ limitations under the License. const {range} = require('./util'); const signup = require('./usecases/signup'); +const toastScenarios = require('./scenarios/toast'); const roomDirectoryScenarios = require('./scenarios/directory'); const lazyLoadingScenarios = require('./scenarios/lazy-loading'); const e2eEncryptionScenarios = require('./scenarios/e2e-encryption'); @@ -37,6 +38,7 @@ module.exports = async function scenario(createSession, restCreator) { const alice = await createUser("alice"); const bob = await createUser("bob"); + await toastScenarios(alice, bob); await roomDirectoryScenarios(alice, bob); await e2eEncryptionScenarios(alice, bob); console.log("create REST users:"); diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js new file mode 100644 index 0000000000..91c6ed750f --- /dev/null +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -0,0 +1,23 @@ +/* +Copyright 2020 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. +*/ + +const {acceptToast} = require("../usecases/toasts"); + +module.exports = async function toastScenarios(alice, bob) { + console.log(" checking and clearing all toasts:"); + await acceptToast(alice, "Help us improve Riot"); + await acceptToast(bob, "Help us improve Riot"); +}; diff --git a/test/end-to-end-tests/src/usecases/join.js b/test/end-to-end-tests/src/usecases/join.js index 10a48bd71c..655c0be686 100644 --- a/test/end-to-end-tests/src/usecases/join.js +++ b/test/end-to-end-tests/src/usecases/join.js @@ -19,15 +19,6 @@ const {openRoomDirectory} = require('./create-room'); module.exports = async function join(session, roomName) { session.log.step(`joins room "${roomName}"`); - while (true) { - try { - const toastDismissButton = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } - await openRoomDirectory(session); const roomInput = await session.query('.mx_DirectorySearchBox input'); await session.replaceInputText(roomInput, roomName); diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js new file mode 100644 index 0000000000..663484ce6e --- /dev/null +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -0,0 +1,45 @@ +/* +Copyright 2020 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. +*/ + +const assert = require('assert'); + +async function assertToast(session, expectedTitle) { + const h2Element = await session.query('.mx_Toast_title h2'); + const toastTitle = await session.innerText(h2Element); + assert(toastTitle, expectedTitle); +} + +async function acceptToast(session, expectedTitle) { + const foundToast = await assertToast(session, expectedTitle); + if (!foundToast) { + throw new Error("could not find expected toast"); + } + + const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_primary'); + await btn.click(); +} + +async function rejectToast(session, expectedTitle) { + const foundToast = await assertToast(session, expectedTitle); + if (!foundToast) { + throw new Error("could not find expected toast"); + } + + const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await btn.click(); +} + +module.exports = {assertToast, acceptToast, rejectToast}; From d9552c7f5c4296ee92258614c261cb3d01352d4f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:10:28 +0100 Subject: [PATCH 355/399] e2e test toasts v2 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 12 ++++++++++-- test/end-to-end-tests/src/usecases/toasts.js | 12 ++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 91c6ed750f..9a0594cc31 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -14,10 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -const {acceptToast} = require("../usecases/toasts"); +const {acceptToast, rejectToast} = require("../usecases/toasts"); module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing all toasts:"); + + alice.log.startGroup(`clears toasts`); + await acceptToast(alice, "Notifications"); await acceptToast(alice, "Help us improve Riot"); - await acceptToast(bob, "Help us improve Riot"); + alice.log.endGroup(); + + bob.log.startGroup(`clears toasts`); + await rejectToast(bob, "Notifications"); + await rejectToast(bob, "Help us improve Riot"); + bob.log.endGroup(); }; diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 663484ce6e..991d3cc39c 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -23,21 +23,13 @@ async function assertToast(session, expectedTitle) { } async function acceptToast(session, expectedTitle) { - const foundToast = await assertToast(session, expectedTitle); - if (!foundToast) { - throw new Error("could not find expected toast"); - } - + await assertToast(session, expectedTitle); const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_primary'); await btn.click(); } async function rejectToast(session, expectedTitle) { - const foundToast = await assertToast(session, expectedTitle); - if (!foundToast) { - throw new Error("could not find expected toast"); - } - + await assertToast(session, expectedTitle); const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await btn.click(); } From 37c875b8635107eb966a692141deed915d14669e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:23:49 +0100 Subject: [PATCH 356/399] improve end to end tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 10 ++++++++-- test/end-to-end-tests/src/usecases/toasts.js | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 9a0594cc31..b5b7fa4494 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -14,18 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -const {acceptToast, rejectToast} = require("../usecases/toasts"); +const {assertNoToasts, acceptToast, rejectToast} = require("../usecases/toasts"); module.exports = async function toastScenarios(alice, bob) { - console.log(" checking and clearing all toasts:"); + console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); + alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); + alice.log.step(`accepts analytics toast`); await acceptToast(alice, "Help us improve Riot"); + await assertNoToasts(alice); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); + alice.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); + alice.log.step(`reject analytics toast`); await rejectToast(bob, "Help us improve Riot"); + await assertNoToasts(bob); bob.log.endGroup(); }; diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 991d3cc39c..636714d66b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -16,6 +16,11 @@ limitations under the License. const assert = require('assert'); +async function assertNoToasts(session) { + const toast = await session.query('.mx_Toast_toast'); + assert(!toast, 'toast found when none expected'); +} + async function assertToast(session, expectedTitle) { const h2Element = await session.query('.mx_Toast_title h2'); const toastTitle = await session.innerText(h2Element); @@ -34,4 +39,4 @@ async function rejectToast(session, expectedTitle) { await btn.click(); } -module.exports = {assertToast, acceptToast, rejectToast}; +module.exports = {assertNoToasts, assertToast, acceptToast, rejectToast}; From faaca43a75f5281ceb335ef1e9514836c0d8c396 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:35:15 +0100 Subject: [PATCH 357/399] more loggin' Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index b5b7fa4494..ee3f2954f3 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,18 +20,32 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); + alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); + alice.log.done(); + alice.log.step(`accepts analytics toast`); await acceptToast(alice, "Help us improve Riot"); + alice.log.done(); + + alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); + alice.log.done(); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - alice.log.step(`reject desktop notifications toast`); + + bob.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); - alice.log.step(`reject analytics toast`); + bob.log.done(); + + bob.log.step(`reject analytics toast`); await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); + + bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); + bob.log.done(); bob.log.endGroup(); }; From 841ce74ac2fde0201b1b698a0b096fd5f5158467 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:39:00 +0100 Subject: [PATCH 358/399] Fix assert vs assert.equal Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/dialog.js | 2 +- test/end-to-end-tests/src/usecases/toasts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/dialog.js b/test/end-to-end-tests/src/usecases/dialog.js index d4ae97dff9..15ac50bb18 100644 --- a/test/end-to-end-tests/src/usecases/dialog.js +++ b/test/end-to-end-tests/src/usecases/dialog.js @@ -20,7 +20,7 @@ const assert = require('assert'); async function assertDialog(session, expectedTitle) { const titleElement = await session.query(".mx_Dialog .mx_Dialog_title"); const dialogHeader = await session.innerText(titleElement); - assert(dialogHeader, expectedTitle); + assert.equal(dialogHeader, expectedTitle); } async function acceptDialog(session, expectedTitle) { diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 636714d66b..a82c910eea 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -24,7 +24,7 @@ async function assertNoToasts(session) { async function assertToast(session, expectedTitle) { const h2Element = await session.query('.mx_Toast_title h2'); const toastTitle = await session.innerText(h2Element); - assert(toastTitle, expectedTitle); + assert.equal(toastTitle, expectedTitle); } async function acceptToast(session, expectedTitle) { From 627b5685887d02ed31f8c1fbecc45f48d8edbbbe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:48:20 +0100 Subject: [PATCH 359/399] check if it is a race Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index ee3f2954f3..980ee4b33f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -29,6 +29,7 @@ module.exports = async function toastScenarios(alice, bob) { await acceptToast(alice, "Help us improve Riot"); alice.log.done(); + await alice.delay(300); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); alice.log.done(); @@ -44,6 +45,7 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(bob, "Help us improve Riot"); bob.log.done(); + await bob.delay(300); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); bob.log.done(); From 61066b0c4c915285f2bb0e5bf8c123cadbcab1f6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:27:09 +0100 Subject: [PATCH 360/399] test it again Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 22 ++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 980ee4b33f..34b979f09a 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,7 +20,6 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); alice.log.done(); @@ -29,14 +28,21 @@ module.exports = async function toastScenarios(alice, bob) { await acceptToast(alice, "Help us improve Riot"); alice.log.done(); - await alice.delay(300); + while (true) { + try { + const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); alice.log.done(); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); bob.log.done(); @@ -45,7 +51,15 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(bob, "Help us improve Riot"); bob.log.done(); - await bob.delay(300); + while (true) { + try { + const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); bob.log.done(); From 91811581bc9412d48842a7341c8f3c33a5623170 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:35:59 +0100 Subject: [PATCH 361/399] fix assertNoToasts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/toasts.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index a82c910eea..2e88677e4b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -17,8 +17,12 @@ limitations under the License. const assert = require('assert'); async function assertNoToasts(session) { - const toast = await session.query('.mx_Toast_toast'); - assert(!toast, 'toast found when none expected'); + try { + await session.query('.mx_Toast_toast'); + } catch (e) { + return; + } + throw new Error('toast found when none expected'); } async function assertToast(session, expectedTitle) { From f3dfdbe74644f974b855a8d494ecbc4d205fd93b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:36:44 +0100 Subject: [PATCH 362/399] debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 34b979f09a..f528e993c3 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -30,6 +30,9 @@ module.exports = async function toastScenarios(alice, bob) { while (true) { try { + const h2Element = await alice.query('.mx_Toast_title h2'); + const toastTitle = await alice.innerText(h2Element); + console.log("DEBUG closing", toastTitle); const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await toastDismissButton.click(); } catch (e) { @@ -53,6 +56,9 @@ module.exports = async function toastScenarios(alice, bob) { while (true) { try { + const h2Element = await bob.query('.mx_Toast_title h2'); + const toastTitle = await bob.innerText(h2Element); + console.log("DEBUG closing", toastTitle); const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await toastDismissButton.click(); } catch (e) { From 63ac1cb4ab583de0eb2869be09f6fc76877f0f37 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:56:24 +0100 Subject: [PATCH 363/399] debug some more Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index f528e993c3..97b5caa14f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,13 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); - await acceptToast(alice, "Notifications"); - alice.log.done(); - - alice.log.step(`accepts analytics toast`); - await acceptToast(alice, "Help us improve Riot"); - alice.log.done(); + // alice.log.step(`accepts desktop notifications toast`); + // await acceptToast(alice, "Notifications"); + // alice.log.done(); + // + // alice.log.step(`accepts analytics toast`); + // await acceptToast(alice, "Help us improve Riot"); + // alice.log.done(); while (true) { try { @@ -46,13 +46,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); - await rejectToast(bob, "Notifications"); - bob.log.done(); - - bob.log.step(`reject analytics toast`); - await rejectToast(bob, "Help us improve Riot"); - bob.log.done(); + // bob.log.step(`reject desktop notifications toast`); + // await rejectToast(bob, "Notifications"); + // bob.log.done(); + // + // bob.log.step(`reject analytics toast`); + // await rejectToast(bob, "Help us improve Riot"); + // bob.log.done(); while (true) { try { From cb07fa53f4282ebfe8f12d63f93bba8344f62b9e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 15:05:40 +0100 Subject: [PATCH 364/399] test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 48 +++++--------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 97b5caa14f..02496f398d 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,25 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - // alice.log.step(`accepts desktop notifications toast`); - // await acceptToast(alice, "Notifications"); - // alice.log.done(); - // - // alice.log.step(`accepts analytics toast`); - // await acceptToast(alice, "Help us improve Riot"); - // alice.log.done(); + alice.log.step(`accepts desktop notifications toast`); + await acceptToast(alice, "Notifications"); + alice.log.done(); - while (true) { - try { - const h2Element = await alice.query('.mx_Toast_title h2'); - const toastTitle = await alice.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + alice.log.step(`rejects analytics toast`); + await rejectToast(alice, "Help us improve Riot"); + alice.log.done(); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -46,25 +34,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - // bob.log.step(`reject desktop notifications toast`); - // await rejectToast(bob, "Notifications"); - // bob.log.done(); - // - // bob.log.step(`reject analytics toast`); - // await rejectToast(bob, "Help us improve Riot"); - // bob.log.done(); + bob.log.step(`reject desktop notifications toast`); + await rejectToast(bob, "Notifications"); + bob.log.done(); - while (true) { - try { - const h2Element = await bob.query('.mx_Toast_title h2'); - const toastTitle = await bob.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + bob.log.step(`reject analytics toast`); + await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); From e35c9d5bbf5eec8e55a6485b275559ef39d7625f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 15:18:43 +0100 Subject: [PATCH 365/399] more testing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/AnalyticsToast.tsx | 2 + test/end-to-end-tests/src/scenarios/toast.js | 48 +++++++++++++++----- test/end-to-end-tests/src/usecases/toasts.js | 5 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index b186a65d9d..7cd59222dd 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -24,12 +24,14 @@ import GenericToast from "../components/views/toasts/GenericToast"; import ToastStore from "../stores/ToastStore"; const onAccept = () => { + console.log("DEBUG onAccept AnalyticsToast"); dis.dispatch({ action: 'accept_cookies', }); }; const onReject = () => { + console.log("DEBUG onReject AnalyticsToast"); dis.dispatch({ action: "reject_cookies", }); diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 02496f398d..97b5caa14f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,13 +20,25 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); - await acceptToast(alice, "Notifications"); - alice.log.done(); + // alice.log.step(`accepts desktop notifications toast`); + // await acceptToast(alice, "Notifications"); + // alice.log.done(); + // + // alice.log.step(`accepts analytics toast`); + // await acceptToast(alice, "Help us improve Riot"); + // alice.log.done(); - alice.log.step(`rejects analytics toast`); - await rejectToast(alice, "Help us improve Riot"); - alice.log.done(); + while (true) { + try { + const h2Element = await alice.query('.mx_Toast_title h2'); + const toastTitle = await alice.innerText(h2Element); + console.log("DEBUG closing", toastTitle); + const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -34,13 +46,25 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); - await rejectToast(bob, "Notifications"); - bob.log.done(); + // bob.log.step(`reject desktop notifications toast`); + // await rejectToast(bob, "Notifications"); + // bob.log.done(); + // + // bob.log.step(`reject analytics toast`); + // await rejectToast(bob, "Help us improve Riot"); + // bob.log.done(); - bob.log.step(`reject analytics toast`); - await rejectToast(bob, "Help us improve Riot"); - bob.log.done(); + while (true) { + try { + const h2Element = await bob.query('.mx_Toast_title h2'); + const toastTitle = await bob.innerText(h2Element); + console.log("DEBUG closing", toastTitle); + const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 2e88677e4b..75142ed08f 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -22,7 +22,10 @@ async function assertNoToasts(session) { } catch (e) { return; } - throw new Error('toast found when none expected'); + + const h2Element = await session.query('.mx_Toast_title h2'); + const toastTitle = await session.innerText(h2Element); + throw new Error(`"${toastTitle}" toast found when none expected`); } async function assertToast(session, expectedTitle) { From 7486338efaa31e9e2657a5458e86388c2a9e5536 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 16:51:04 +0100 Subject: [PATCH 366/399] Fix.the.tests. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 48 +++++--------------- test/end-to-end-tests/src/session.js | 4 +- test/end-to-end-tests/src/usecases/toasts.js | 9 ++-- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 97b5caa14f..1206ef40b0 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,25 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - // alice.log.step(`accepts desktop notifications toast`); - // await acceptToast(alice, "Notifications"); - // alice.log.done(); - // - // alice.log.step(`accepts analytics toast`); - // await acceptToast(alice, "Help us improve Riot"); - // alice.log.done(); + alice.log.step(`reject desktop notifications toast`); + await rejectToast(alice, "Notifications"); + alice.log.done(); - while (true) { - try { - const h2Element = await alice.query('.mx_Toast_title h2'); - const toastTitle = await alice.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + alice.log.step(`accepts analytics toast`); + await acceptToast(alice, "Help us improve Riot"); + alice.log.done(); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -46,25 +34,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - // bob.log.step(`reject desktop notifications toast`); - // await rejectToast(bob, "Notifications"); - // bob.log.done(); - // - // bob.log.step(`reject analytics toast`); - // await rejectToast(bob, "Help us improve Riot"); - // bob.log.done(); + bob.log.step(`reject desktop notifications toast`); + await rejectToast(bob, "Notifications"); + bob.log.done(); - while (true) { - try { - const h2Element = await bob.query('.mx_Toast_title h2'); - const toastTitle = await bob.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + bob.log.step(`reject analytics toast`); + await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); diff --git a/test/end-to-end-tests/src/session.js b/test/end-to-end-tests/src/session.js index 55c2ed440c..907ee2fb8e 100644 --- a/test/end-to-end-tests/src/session.js +++ b/test/end-to-end-tests/src/session.js @@ -122,8 +122,8 @@ module.exports = class RiotSession { await input.type(text); } - query(selector, timeout = DEFAULT_TIMEOUT) { - return this.page.waitForSelector(selector, {visible: true, timeout}); + query(selector, timeout = DEFAULT_TIMEOUT, hidden = false) { + return this.page.waitForSelector(selector, {visible: true, timeout, hidden}); } async queryAll(selector) { diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 75142ed08f..204ed2b983 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -18,14 +18,13 @@ const assert = require('assert'); async function assertNoToasts(session) { try { - await session.query('.mx_Toast_toast'); + await session.query('.mx_Toast_toast', 1000, true); } catch (e) { - return; + const h2Element = await session.query('.mx_Toast_title h2', 1000); + const toastTitle = await session.innerText(h2Element); + throw new Error(`"${toastTitle}" toast found when none expected`); } - const h2Element = await session.query('.mx_Toast_title h2'); - const toastTitle = await session.innerText(h2Element); - throw new Error(`"${toastTitle}" toast found when none expected`); } async function assertToast(session, expectedTitle) { From 71108fcf393e4648d1dfc1fd17e0b50c8423ed12 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 17:02:32 +0100 Subject: [PATCH 367/399] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/toasts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 204ed2b983..db78352f2b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -24,7 +24,6 @@ async function assertNoToasts(session) { const toastTitle = await session.innerText(h2Element); throw new Error(`"${toastTitle}" toast found when none expected`); } - } async function assertToast(session, expectedTitle) { From 73c35ff80e0a1d9a6323cbdd49df5a7fe4f83cbc Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 28 May 2020 00:05:45 -0400 Subject: [PATCH 368/399] set the client's pickle key if the platform can store one --- src/BasePlatform.js | 31 +++++++++++++++++++++++++++++++ src/Lifecycle.js | 15 ++++++++++++--- src/MatrixClientPeg.js | 1 + 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 8a950dc2e3..f4fa43cb10 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -190,4 +190,35 @@ export default class BasePlatform { onKeyDown(ev: KeyboardEvent): boolean { return false; // no shortcuts implemented } + + /** + * Get a previously stored pickle key. The pickle key is used for + * encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the previously stored pickle key, or null if no + * pickle key has been stored. + */ + async getPickleKey(userId: string, deviceId: string): string | null { + return null; + } + + /** + * Create and store a pickle key for encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the pickle key, or null if the platform does not + * support storing pickle keys. + */ + async createPickleKey(userId: string, deviceId: string): string | null { + return null; + } + + /** + * Delete a previously stored pickle key from storage. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + */ + async destroyPickleKey(userId: string, deviceId: string) { + } } diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1baa6c8e0c..598624293b 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -298,6 +298,8 @@ async function _restoreFromLocalStorage(opts) { return false; } + const pickleKey = await PlatformPeg.get().getPickleKey(userId, deviceId); + console.log(`Restoring session for ${userId}`); await _doSetLoggedIn({ userId: userId, @@ -306,6 +308,7 @@ async function _restoreFromLocalStorage(opts) { homeserverUrl: hsUrl, identityServerUrl: isUrl, guest: isGuest, + pickleKey: pickleKey, }, false); return true; } else { @@ -348,9 +351,13 @@ async function _handleLoadSessionFailure(e) { * * @returns {Promise} promise which resolves to the new MatrixClient once it has been started */ -export function setLoggedIn(credentials) { +export async function setLoggedIn(credentials) { stopMatrixClient(); - return _doSetLoggedIn(credentials, true); + const pickleKey = credentials.userId && credentials.deviceId + ? await PlatformPeg.get().createPickleKey(credentials.userId, credentials.deviceId) + : null; + + return _doSetLoggedIn(Object.assign({}, credentials, {pickleKey}), true); } /** @@ -516,7 +523,9 @@ export function logout() { } _isLoggingOut = true; - MatrixClientPeg.get().logout().then(onLoggedOut, + const client = MatrixClientPeg.get(); + PlatformPeg.get().destroyPickleKey(client.getUserId(), client.getDeviceId()); + client.logout().then(onLoggedOut, (err) => { // Just throwing an error here is going to be very unhelpful // if you're trying to log out because your server's down and diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 21f05b9759..af43705227 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -218,6 +218,7 @@ class _MatrixClientPeg { accessToken: creds.accessToken, userId: creds.userId, deviceId: creds.deviceId, + pickleKey: creds.pickleKey, timelineSupport: true, forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'), From 81922231ed638764083610889b7ffafc3a7467ad Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 28 May 2020 13:55:07 +0100 Subject: [PATCH 369/399] Move Appearance tab to ts --- src/components/structures/MatrixChat.tsx | 4 +- src/components/views/elements/Field.tsx | 2 +- ...gsTab.js => AppearanceUserSettingsTab.tsx} | 133 ++++++++++------- src/dispatcher/actions.ts | 5 + .../payloads/RecheckThemePayload.ts | 27 ++++ .../watchers/FontWatcher.ts} | 36 +++-- src/settings/watchers/ThemeWatcher.ts | 138 ++++++++++++++++++ src/settings/watchers/Watcher.ts | 20 +++ src/theme.js | 110 +------------- 9 files changed, 295 insertions(+), 180 deletions(-) rename src/components/views/settings/tabs/user/{AppearanceUserSettingsTab.js => AppearanceUserSettingsTab.tsx} (73%) create mode 100644 src/dispatcher/payloads/RecheckThemePayload.ts rename src/{FontWatcher.js => settings/watchers/FontWatcher.ts} (51%) create mode 100644 src/settings/watchers/ThemeWatcher.ts create mode 100644 src/settings/watchers/Watcher.ts diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 1200fcaf7a..f6848feb03 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -58,8 +58,8 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils"; import DMRoomMap from '../../utils/DMRoomMap'; import { countRoomsWithNotif } from '../../RoomNotifs'; -import { ThemeWatcher } from "../../theme"; -import { FontWatcher } from '../../FontWatcher'; +import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; +import { FontWatcher } from '../../settings/watchers/FontWatcher'; import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer, IDeferred } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; diff --git a/src/components/views/elements/Field.tsx b/src/components/views/elements/Field.tsx index 39ed2df9f9..771d2182ea 100644 --- a/src/components/views/elements/Field.tsx +++ b/src/components/views/elements/Field.tsx @@ -34,7 +34,7 @@ interface IProps extends React.InputHTMLAttributes