From d282675bc643058c4835db499e68d7b34af0e13f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 13 Oct 2019 15:08:50 +0300 Subject: [PATCH 0001/4306] Improve reply rendering Signed-off-by: Tulir Asokan --- res/css/_components.scss | 1 + res/css/views/elements/_ReplyThread.scss | 11 +- res/css/views/rooms/_ReplyPreview.scss | 7 +- res/css/views/rooms/_ReplyTile.scss | 96 +++++++ src/components/views/elements/ReplyThread.js | 15 +- .../views/messages/MImageReplyBody.js | 33 +++ src/components/views/messages/MessageEvent.js | 9 +- src/components/views/rooms/ReplyPreview.js | 12 +- src/components/views/rooms/ReplyTile.js | 234 ++++++++++++++++++ 9 files changed, 388 insertions(+), 30 deletions(-) create mode 100644 res/css/views/rooms/_ReplyTile.scss create mode 100644 src/components/views/messages/MImageReplyBody.js create mode 100644 src/components/views/rooms/ReplyTile.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 4891fd90c0..2c54c5f37f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -153,6 +153,7 @@ @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; +@import "./views/rooms/_ReplyTile.scss"; @import "./views/rooms/_RoomBreadcrumbs.scss"; @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyThread.scss index bf44a11728..0d53a6b6f4 100644 --- a/res/css/views/elements/_ReplyThread.scss +++ b/res/css/views/elements/_ReplyThread.scss @@ -18,20 +18,13 @@ limitations under the License. margin-top: 0; } -.mx_ReplyThread .mx_DateSeparator { - font-size: 1em !important; - margin-top: 0; - margin-bottom: 0; - padding-bottom: 1px; - bottom: -5px; -} - .mx_ReplyThread_show { cursor: pointer; } blockquote.mx_ReplyThread { margin-left: 0; + margin-bottom: 8px; padding-left: 10px; - border-left: 4px solid $blockquote-bar-color; + border-left: 4px solid $button-bg-color; } diff --git a/res/css/views/rooms/_ReplyPreview.scss b/res/css/views/rooms/_ReplyPreview.scss index 4dc4cb2c40..08fbd27808 100644 --- a/res/css/views/rooms/_ReplyPreview.scss +++ b/res/css/views/rooms/_ReplyPreview.scss @@ -32,12 +32,16 @@ limitations under the License. } .mx_ReplyPreview_header { - margin: 12px; + margin: 8px; color: $primary-fg-color; font-weight: 400; opacity: 0.4; } +.mx_ReplyPreview_tile { + margin: 0 8px; +} + .mx_ReplyPreview_title { float: left; } @@ -45,6 +49,7 @@ limitations under the License. .mx_ReplyPreview_cancel { float: right; cursor: pointer; + display: flex; } .mx_ReplyPreview_clear { diff --git a/res/css/views/rooms/_ReplyTile.scss b/res/css/views/rooms/_ReplyTile.scss new file mode 100644 index 0000000000..0a055297c6 --- /dev/null +++ b/res/css/views/rooms/_ReplyTile.scss @@ -0,0 +1,96 @@ +/* +Copyright 2019 Tulir Asokan + +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_ReplyTile { + max-width: 100%; + clear: both; + padding-top: 2px; + padding-bottom: 2px; + font-size: 14px; + position: relative; + line-height: 16px; +} + +.mx_ReplyTile > a { + display: block; + text-decoration: none; + color: $primary-fg-color; +} + +// We do reply size limiting with CSS to avoid duplicating the TextualBody component. +.mx_ReplyTile .mx_EventTile_content { + $reply-lines: 2; + $line-height: 22px; + + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: $reply-lines; + line-height: $line-height; + + .mx_EventTile_body.mx_EventTile_bigEmoji { + line-height: $line-height !important; + // Override the big emoji override + font-size: 14px !important; + } +} + +.mx_ReplyTile.mx_ReplyTile_info { + padding-top: 0px; +} + +.mx_ReplyTile .mx_SenderProfile { + color: $primary-fg-color; + font-size: 14px; + display: inline-block; /* anti-zalgo, with overflow hidden */ + overflow: hidden; + cursor: pointer; + padding-left: 0px; /* left gutter */ + padding-bottom: 0px; + padding-top: 0px; + margin: 0px; + line-height: 17px; + /* the next three lines, along with overflow hidden, truncate long display names */ + white-space: nowrap; + text-overflow: ellipsis; + max-width: calc(100% - 65px); +} + +.mx_ReplyTile_redacted .mx_UnknownBody { + --lozenge-color: $event-redacted-fg-color; + --lozenge-border-color: $event-redacted-border-color; + display: block; + height: 22px; + width: 250px; + border-radius: 11px; + background: + repeating-linear-gradient( + -45deg, + var(--lozenge-color), + var(--lozenge-color) 3px, + transparent 3px, + transparent 6px + ); + box-shadow: 0px 0px 3px var(--lozenge-border-color) inset; +} + +.mx_ReplyTile_sending.mx_ReplyTile_redacted .mx_UnknownBody { + opacity: 0.4; +} + +.mx_ReplyTile_contextual { + opacity: 0.4; +} diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index fac0a71617..1764c008fa 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -304,20 +304,11 @@ export default class ReplyThread extends React.Component { header = ; } - const EventTile = sdk.getComponent('views.rooms.EventTile'); - const DateSeparator = sdk.getComponent('messages.DateSeparator'); + const ReplyTile = sdk.getComponent('views.rooms.ReplyTile'); const evTiles = this.state.events.map((ev) => { - let dateSep = null; - - if (wantsDateSeparator(this.props.parentEv.getDate(), ev.getDate())) { - dateSep = ; - } - return
- { dateSep } - ; }); - return
+ return
{ header }
{ evTiles }
; diff --git a/src/components/views/messages/MImageReplyBody.js b/src/components/views/messages/MImageReplyBody.js new file mode 100644 index 0000000000..bb869919fc --- /dev/null +++ b/src/components/views/messages/MImageReplyBody.js @@ -0,0 +1,33 @@ +/* +Copyright 2019 Tulir Asokan + +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 MImageBody from './MImageBody'; + +export default class MImageReplyBody extends MImageBody { + onClick(ev) { + ev.preventDefault(); + } + + wrapImage(contentUrl, children) { + return children; + } + + // Don't show "Download this_file.png ..." + getFileBody() { + return null; + } +} diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index a616dd96ed..28f2a471bb 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -43,6 +43,9 @@ module.exports = createReactClass({ /* the maximum image height to use, if the event is an image */ maxImageHeight: PropTypes.number, + + overrideBodyTypes: PropTypes.object, + overrideEventTypes: PropTypes.object, }, getEventTileOps: function() { @@ -60,9 +63,11 @@ module.exports = createReactClass({ 'm.file': sdk.getComponent('messages.MFileBody'), 'm.audio': sdk.getComponent('messages.MAudioBody'), 'm.video': sdk.getComponent('messages.MVideoBody'), + ...(this.props.overrideBodyTypes || {}), }; const evTypes = { 'm.sticker': sdk.getComponent('messages.MStickerBody'), + ...(this.props.overrideEventTypes || {}), }; const content = this.props.mxEvent.getContent(); @@ -81,7 +86,7 @@ module.exports = createReactClass({ } } - return ; + onHeightChanged={this.props.onHeightChanged} /> : null; }, }); diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index caf8feeea2..a69a286a15 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -68,7 +68,7 @@ export default class ReplyPreview extends React.Component { render() { if (!this.state.event) return null; - const EventTile = sdk.getComponent('rooms.EventTile'); + const ReplyTile = sdk.getComponent('rooms.ReplyTile'); return
@@ -80,11 +80,11 @@ export default class ReplyPreview extends React.Component { onClick={cancelQuoting} />
- +
+ +
; } diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js new file mode 100644 index 0000000000..5a56ba9dc1 --- /dev/null +++ b/src/components/views/rooms/ReplyTile.js @@ -0,0 +1,234 @@ +/* +Copyright 2019 Tulir Asokan + +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'; +const classNames = require("classnames"); +import { _t, _td } from '../../../languageHandler'; + +const sdk = require('../../../index'); + +import dis from '../../../dispatcher'; +import SettingsStore from "../../../settings/SettingsStore"; +import {MatrixClient} from 'matrix-js-sdk'; + +const ObjectUtils = require('../../../ObjectUtils'); + +const eventTileTypes = { + 'm.room.message': 'messages.MessageEvent', + 'm.sticker': 'messages.MessageEvent', + 'm.call.invite': 'messages.TextualEvent', + 'm.call.answer': 'messages.TextualEvent', + 'm.call.hangup': 'messages.TextualEvent', +}; + +const stateEventTileTypes = { + 'm.room.aliases': 'messages.TextualEvent', + // 'm.room.aliases': 'messages.RoomAliasesEvent', // too complex + 'm.room.canonical_alias': 'messages.TextualEvent', + 'm.room.create': 'messages.RoomCreate', + 'm.room.member': 'messages.TextualEvent', + 'm.room.name': 'messages.TextualEvent', + 'm.room.avatar': 'messages.RoomAvatarEvent', + 'm.room.third_party_invite': 'messages.TextualEvent', + 'm.room.history_visibility': 'messages.TextualEvent', + 'm.room.encryption': 'messages.TextualEvent', + 'm.room.topic': 'messages.TextualEvent', + 'm.room.power_levels': 'messages.TextualEvent', + 'm.room.pinned_events': 'messages.TextualEvent', + 'm.room.server_acl': 'messages.TextualEvent', + 'im.vector.modular.widgets': 'messages.TextualEvent', + 'm.room.tombstone': 'messages.TextualEvent', + 'm.room.join_rules': 'messages.TextualEvent', + 'm.room.guest_access': 'messages.TextualEvent', + 'm.room.related_groups': 'messages.TextualEvent', +}; + +function getHandlerTile(ev) { + const type = ev.getType(); + return ev.isState() ? stateEventTileTypes[type] : eventTileTypes[type]; +} + +class ReplyTile extends React.Component { + static contextTypes = { + matrixClient: PropTypes.instanceOf(MatrixClient).isRequired, + } + + static propTypes = { + mxEvent: PropTypes.object.isRequired, + isRedacted: PropTypes.bool, + permalinkCreator: PropTypes.object, + onHeightChanged: PropTypes.func, + } + + static defaultProps = { + onHeightChanged: function() {}, + } + + constructor(props, context) { + super(props, context); + this.state = {}; + this.onClick = this.onClick.bind(this); + } + + componentDidMount() { + this.props.mxEvent.on("Event.decrypted", this._onDecrypted); + } + + shouldComponentUpdate(nextProps, nextState) { + if (!ObjectUtils.shallowEqual(this.state, nextState)) { + return true; + } + + return !this._propsEqual(this.props, nextProps); + } + + componentWillUnmount() { + const client = this.context.matrixClient; + this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted); + } + + _onDecrypted() { + this.forceUpdate(); + } + + _propsEqual(objA, objB) { + const keysA = Object.keys(objA); + const keysB = Object.keys(objB); + + if (keysA.length !== keysB.length) { + return false; + } + + for (let i = 0; i < keysA.length; i++) { + const key = keysA[i]; + + if (!objB.hasOwnProperty(key)) { + return false; + } + + if (objA[key] !== objB[key]) { + return false; + } + } + return true; + } + + onClick(e) { + // This allows the permalink to be opened in a new tab/window or copied as + // matrix.to, but also for it to enable routing within Riot when clicked. + e.preventDefault(); + dis.dispatch({ + action: 'view_room', + event_id: this.props.mxEvent.getId(), + highlighted: true, + room_id: this.props.mxEvent.getRoomId(), + }); + } + + render() { + const SenderProfile = sdk.getComponent('messages.SenderProfile'); + + const content = this.props.mxEvent.getContent(); + const msgtype = content.msgtype; + const eventType = this.props.mxEvent.getType(); + + // Info messages are basically information about commands processed on a room + let isInfoMessage = ( + eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType !== 'm.room.create' + ); + + let tileHandler = getHandlerTile(this.props.mxEvent); + // If we're showing hidden events in the timeline, we should use the + // source tile when there's no regular tile for an event and also for + // replace relations (which otherwise would display as a confusing + // duplicate of the thing they are replacing). + const useSource = !tileHandler || this.props.mxEvent.isRelation("m.replace"); + if (useSource && SettingsStore.getValue("showHiddenEventsInTimeline")) { + tileHandler = "messages.ViewSourceEvent"; + // Reuse info message avatar and sender profile styling + isInfoMessage = true; + } + // This shouldn't happen: the caller should check we support this type + // before trying to instantiate us + if (!tileHandler) { + const {mxEvent} = this.props; + console.warn(`Event type not supported: type:${mxEvent.getType()} isState:${mxEvent.isState()}`); + return
+ { _t('This event could not be displayed') } +
; + } + const EventTileType = sdk.getComponent(tileHandler); + + const classes = classNames({ + mx_ReplyTile: true, + mx_ReplyTile_info: isInfoMessage, + mx_ReplyTile_redacted: this.props.isRedacted, + }); + + let permalink = "#"; + if (this.props.permalinkCreator) { + permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); + } + + let sender; + let needsSenderProfile = tileHandler !== 'messages.RoomCreate' && !isInfoMessage; + + if (needsSenderProfile) { + let text = null; + if (msgtype === 'm.image') text = _td('%(senderName)s sent an image'); + else if (msgtype === 'm.video') text = _td('%(senderName)s sent a video'); + else if (msgtype === 'm.file') text = _td('%(senderName)s uploaded a file'); + sender = ; + } + + const MImageReplyBody = sdk.getComponent('messages.MImageReplyBody'); + const TextualBody = sdk.getComponent('messages.TextualBody'); + const msgtypeOverrides = { + "m.image": MImageReplyBody, + // We don't want a download link for files, just the file name is enough. + "m.file": TextualBody, + "m.sticker": TextualBody, + "m.audio": TextualBody, + "m.video": TextualBody, + }; + const evOverrides = { + "m.sticker": TextualBody, + }; + + return ( + + ) + } +} + +module.exports = ReplyTile; From 03d36f30ec1093dab132c8c2bbe9414da00cb9b2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 5 Mar 2020 13:44:54 +0200 Subject: [PATCH 0002/4306] Fix lint errors --- src/components/views/elements/ReplyThread.js | 1 - src/components/views/messages/MImageReplyBody.js | 1 - src/components/views/messages/MessageEvent.js | 2 +- src/components/views/rooms/ReplyPreview.js | 1 - src/components/views/rooms/ReplyTile.js | 7 +++---- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 25b39a2ad4..954c6b49c4 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -20,7 +20,6 @@ import * as sdk from '../../../index'; import {_t} from '../../../languageHandler'; import PropTypes from 'prop-types'; import dis from '../../../dispatcher'; -import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent} from 'matrix-js-sdk'; import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; import SettingsStore from "../../../settings/SettingsStore"; diff --git a/src/components/views/messages/MImageReplyBody.js b/src/components/views/messages/MImageReplyBody.js index bb869919fc..31b4d1fa82 100644 --- a/src/components/views/messages/MImageReplyBody.js +++ b/src/components/views/messages/MImageReplyBody.js @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; import MImageBody from './MImageBody'; export default class MImageReplyBody extends MImageBody { diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index 14ab3c8757..3703d3a629 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -123,6 +123,6 @@ export default createReactClass({ editState={this.props.editState} onHeightChanged={this.props.onHeightChanged} onMessageAllowed={this.onTileUpdate} - /> : null + /> : null; }, }); diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 92e3f123a0..a22a85a2f1 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -19,7 +19,6 @@ import dis from '../../../dispatcher'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import RoomViewStore from '../../../stores/RoomViewStore'; -import SettingsStore from "../../../settings/SettingsStore"; import PropTypes from "prop-types"; import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js index 5a56ba9dc1..36cb07f092 100644 --- a/src/components/views/rooms/ReplyTile.js +++ b/src/components/views/rooms/ReplyTile.js @@ -97,7 +97,6 @@ class ReplyTile extends React.Component { } componentWillUnmount() { - const client = this.context.matrixClient; this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted); } @@ -185,7 +184,7 @@ class ReplyTile extends React.Component { } let sender; - let needsSenderProfile = tileHandler !== 'messages.RoomCreate' && !isInfoMessage; + const needsSenderProfile = tileHandler !== 'messages.RoomCreate' && !isInfoMessage; if (needsSenderProfile) { let text = null; @@ -224,10 +223,10 @@ class ReplyTile extends React.Component { showUrlPreview={false} overrideBodyTypes={msgtypeOverrides} overrideEventTypes={evOverrides} - maxImageHeight={96}/> + maxImageHeight={96} />
- ) + ); } } From 03299a28a4f27e96a5b9b0351945b3b9c3c5218d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 14:23:34 +0300 Subject: [PATCH 0003/4306] Fix import/export things --- src/components/views/rooms/ReplyTile.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js index 36cb07f092..3ad6962f1a 100644 --- a/src/components/views/rooms/ReplyTile.js +++ b/src/components/views/rooms/ReplyTile.js @@ -1,5 +1,5 @@ /* -Copyright 2019 Tulir Asokan +Copyright 2020 Tulir Asokan Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,16 +16,16 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -const classNames = require("classnames"); +import classNames from 'classnames'; import { _t, _td } from '../../../languageHandler'; -const sdk = require('../../../index'); +import * as sdk from '../../../index'; import dis from '../../../dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; import {MatrixClient} from 'matrix-js-sdk'; -const ObjectUtils = require('../../../ObjectUtils'); +import * as ObjectUtils from '../../../ObjectUtils'; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -230,4 +230,4 @@ class ReplyTile extends React.Component { } } -module.exports = ReplyTile; +export default ReplyTile; From e64ff0f099ac6660e596fc640a839c9f76f9f79b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 14:39:16 +0300 Subject: [PATCH 0004/4306] Change score color to match sender name --- res/css/views/elements/_ReplyThread.scss | 32 ++++++++++++++++++++ src/components/views/elements/ReplyThread.js | 9 ++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyThread.scss index 0d53a6b6f4..d5388e4631 100644 --- a/res/css/views/elements/_ReplyThread.scss +++ b/res/css/views/elements/_ReplyThread.scss @@ -27,4 +27,36 @@ blockquote.mx_ReplyThread { margin-bottom: 8px; padding-left: 10px; border-left: 4px solid $button-bg-color; + + &.mx_ReplyThread_color1 { + border-left-color: $username-variant1-color; + } + + &.mx_ReplyThread_color2 { + border-left-color: $username-variant2-color; + } + + &.mx_ReplyThread_color3 { + border-left-color: $username-variant3-color; + } + + &.mx_ReplyThread_color4 { + border-left-color: $username-variant4-color; + } + + &.mx_ReplyThread_color5 { + border-left-color: $username-variant5-color; + } + + &.mx_ReplyThread_color6 { + border-left-color: $username-variant6-color; + } + + &.mx_ReplyThread_color7 { + border-left-color: $username-variant7-color; + } + + &.mx_ReplyThread_color8 { + border-left-color: $username-variant8-color; + } } diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 976b3a8815..92e87ad945 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -25,6 +25,7 @@ import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks import SettingsStore from "../../../settings/SettingsStore"; import escapeHtml from "escape-html"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import { getUserNameColorClass } from "../../../utils/FormattingUtils" // This component does no cycle detection, simply because the only way to make such a cycle would be to // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would @@ -285,6 +286,10 @@ export default class ReplyThread extends React.Component { dis.dispatch({action: 'focus_composer'}); } + getReplyThreadColorClass(ev) { + return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyThread"); + } + render() { let header = null; @@ -299,7 +304,7 @@ export default class ReplyThread extends React.Component { const ev = this.state.loadedEv; const Pill = sdk.getComponent('elements.Pill'); const room = this.context.getRoom(ev.getRoomId()); - header =
+ header =
{ _t('In reply to ', {}, { 'a': (sub) => { sub }, @@ -315,7 +320,7 @@ export default class ReplyThread extends React.Component { const ReplyTile = sdk.getComponent('views.rooms.ReplyTile'); const evTiles = this.state.events.map((ev) => { - return
+ return
Date: Fri, 10 Apr 2020 14:52:24 +0300 Subject: [PATCH 0005/4306] Add missing semicolon --- src/components/views/elements/ReplyThread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 92e87ad945..770f95f9dc 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -25,7 +25,7 @@ import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks import SettingsStore from "../../../settings/SettingsStore"; import escapeHtml from "escape-html"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import { getUserNameColorClass } from "../../../utils/FormattingUtils" +import { getUserNameColorClass } from "../../../utils/FormattingUtils"; // This component does no cycle detection, simply because the only way to make such a cycle would be to // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would From 25af26323c3ca9048ab7a45d63ed74116e553aa8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 15:45:59 +0300 Subject: [PATCH 0006/4306] Make image reply rendering even more compact --- res/css/_components.scss | 1 + res/css/views/messages/_MImageReplyBody.scss | 35 +++++++++++++++++++ .../views/messages/MImageReplyBody.js | 31 ++++++++++++++-- src/components/views/rooms/ReplyTile.js | 2 +- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 res/css/views/messages/_MImageReplyBody.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 607257400b..2d701bb1e1 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -130,6 +130,7 @@ @import "./views/messages/_MEmoteBody.scss"; @import "./views/messages/_MFileBody.scss"; @import "./views/messages/_MImageBody.scss"; +@import "./views/messages/_MImageReplyBody.scss"; @import "./views/messages/_MNoticeBody.scss"; @import "./views/messages/_MStickerBody.scss"; @import "./views/messages/_MTextBody.scss"; diff --git a/res/css/views/messages/_MImageReplyBody.scss b/res/css/views/messages/_MImageReplyBody.scss new file mode 100644 index 0000000000..8169e027d1 --- /dev/null +++ b/res/css/views/messages/_MImageReplyBody.scss @@ -0,0 +1,35 @@ +/* +Copyright 2020 Tulir Asokan + +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_MImageReplyBody { + display: grid; + grid-template: "image sender" 20px + "image filename" 20px + / 44px auto; + grid-gap: 4px; +} + +.mx_MImageReplyBody_thumbnail { + grid-area: image; +} + +.mx_MImageReplyBody_sender { + grid-area: sender; +} + +.mx_MImageReplyBody_filename { + grid-area: filename; +} diff --git a/src/components/views/messages/MImageReplyBody.js b/src/components/views/messages/MImageReplyBody.js index 31b4d1fa82..cdc78e46e8 100644 --- a/src/components/views/messages/MImageReplyBody.js +++ b/src/components/views/messages/MImageReplyBody.js @@ -1,5 +1,5 @@ /* -Copyright 2019 Tulir Asokan +Copyright 2020 Tulir Asokan Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from "react"; +import { _td } from "../../../languageHandler"; +import * as sdk from "../../../index"; import MImageBody from './MImageBody'; +import MFileBody from "./MFileBody"; export default class MImageReplyBody extends MImageBody { onClick(ev) { @@ -27,6 +31,29 @@ export default class MImageReplyBody extends MImageBody { // Don't show "Download this_file.png ..." getFileBody() { - return null; + return MFileBody.prototype.presentableTextForFile.call(this, this.props.mxEvent.getContent()); + } + + render() { + if (this.state.error !== null) { + return super.render(); + } + + const content = this.props.mxEvent.getContent(); + + const contentUrl = this._getContentUrl(); + const thumbnail = this._messageContent(contentUrl, this._getThumbUrl(), content); + const fileBody = this.getFileBody(); + const SenderProfile = sdk.getComponent('messages.SenderProfile'); + const sender = ; + + return
+
{ thumbnail }
+
{ sender }
+
{ fileBody }
+
; } } diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js index 3ad6962f1a..ca349baac2 100644 --- a/src/components/views/rooms/ReplyTile.js +++ b/src/components/views/rooms/ReplyTile.js @@ -184,7 +184,7 @@ class ReplyTile extends React.Component { } let sender; - const needsSenderProfile = tileHandler !== 'messages.RoomCreate' && !isInfoMessage; + const needsSenderProfile = msgtype !== 'm.image' && tileHandler !== 'messages.RoomCreate' && !isInfoMessage; if (needsSenderProfile) { let text = null; From ec7acd1c0fbaf5d96415f380a3b85b54d079aa9f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 15:56:09 +0300 Subject: [PATCH 0007/4306] Fix rendering reply after event is decrypted --- src/components/views/rooms/ReplyTile.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js index ca349baac2..34b2c6ad38 100644 --- a/src/components/views/rooms/ReplyTile.js +++ b/src/components/views/rooms/ReplyTile.js @@ -82,6 +82,7 @@ class ReplyTile extends React.Component { super(props, context); this.state = {}; this.onClick = this.onClick.bind(this); + this._onDecrypted = this._onDecrypted.bind(this); } componentDidMount() { @@ -102,6 +103,9 @@ class ReplyTile extends React.Component { _onDecrypted() { this.forceUpdate(); + if (this.props.onHeightChanged) { + this.props.onHeightChanged(); + } } _propsEqual(objA, objB) { From da3bd5ebee68dc15f04e15c3b55183f769413ce9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 16:03:27 +0300 Subject: [PATCH 0008/4306] Disable pointer events inside replies --- res/css/views/rooms/_ReplyTile.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/rooms/_ReplyTile.scss b/res/css/views/rooms/_ReplyTile.scss index 0a055297c6..a6cff00ff2 100644 --- a/res/css/views/rooms/_ReplyTile.scss +++ b/res/css/views/rooms/_ReplyTile.scss @@ -35,6 +35,8 @@ limitations under the License. $reply-lines: 2; $line-height: 22px; + pointer-events: none; + text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; From 6b96a161087f4cda8ab6dcafd155e2d689a5adff Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2020 16:18:06 +0300 Subject: [PATCH 0009/4306] Add absolute max height and some improvements for
 replies

---
 res/css/views/rooms/_ReplyTile.scss | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/res/css/views/rooms/_ReplyTile.scss b/res/css/views/rooms/_ReplyTile.scss
index a6cff00ff2..70a383a1cf 100644
--- a/res/css/views/rooms/_ReplyTile.scss
+++ b/res/css/views/rooms/_ReplyTile.scss
@@ -34,6 +34,7 @@ limitations under the License.
 .mx_ReplyTile .mx_EventTile_content {
     $reply-lines: 2;
     $line-height: 22px;
+    $max-height: 66px;
 
     pointer-events: none;
 
@@ -42,12 +43,26 @@ limitations under the License.
     -webkit-box-orient: vertical;
     -webkit-line-clamp: $reply-lines;
     line-height: $line-height;
+    max-height: $max-height;
 
     .mx_EventTile_body.mx_EventTile_bigEmoji {
         line-height: $line-height !important;
         // Override the big emoji override
         font-size: 14px !important;
     }
+
+    // Hack to cut content in 
 tags too
+    .mx_EventTile_pre_container > pre {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        display: -webkit-box;
+        -webkit-box-orient: vertical;
+        -webkit-line-clamp: $reply-lines;
+        padding: 4px;
+    }
+    .markdown-body blockquote, .markdown-body dl, .markdown-body ol, .markdown-body p, .markdown-body pre, .markdown-body table, .markdown-body ul {
+        margin-bottom: 4px;
+    }
 }
 
 .mx_ReplyTile.mx_ReplyTile_info {

From e7ad9b82e0f42e6c7ac5511ade135e71a273e414 Mon Sep 17 00:00:00 2001
From: Tulir Asokan 
Date: Fri, 10 Apr 2020 16:27:39 +0300
Subject: [PATCH 0010/4306] Fix stylelint issue and update header

---
 res/css/views/messages/_MImageReplyBody.scss | 7 ++++---
 res/css/views/rooms/_ReplyTile.scss          | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/res/css/views/messages/_MImageReplyBody.scss b/res/css/views/messages/_MImageReplyBody.scss
index 8169e027d1..9b25b4392a 100644
--- a/res/css/views/messages/_MImageReplyBody.scss
+++ b/res/css/views/messages/_MImageReplyBody.scss
@@ -16,9 +16,10 @@ limitations under the License.
 
 .mx_MImageReplyBody {
     display: grid;
-    grid-template: "image sender"   20px
-                   "image filename" 20px
-                  / 44px  auto;
+    grid-template:
+        "image sender"   20px
+        "image filename" 20px
+        / 44px  auto;
     grid-gap: 4px;
 }
 
diff --git a/res/css/views/rooms/_ReplyTile.scss b/res/css/views/rooms/_ReplyTile.scss
index 70a383a1cf..fd68430157 100644
--- a/res/css/views/rooms/_ReplyTile.scss
+++ b/res/css/views/rooms/_ReplyTile.scss
@@ -1,5 +1,5 @@
 /*
-Copyright 2019 Tulir Asokan 
+Copyright 2020 Tulir Asokan 
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.

From b554d59ed165d68b56a9b08faadeec86d2f7c2b7 Mon Sep 17 00:00:00 2001
From: Tulir Asokan 
Date: Fri, 10 Apr 2020 17:05:29 +0300
Subject: [PATCH 0011/4306] Prevent reply thumbnail image from overflowing

---
 res/css/views/messages/_MImageReplyBody.scss | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/res/css/views/messages/_MImageReplyBody.scss b/res/css/views/messages/_MImageReplyBody.scss
index 9b25b4392a..8c5cb97478 100644
--- a/res/css/views/messages/_MImageReplyBody.scss
+++ b/res/css/views/messages/_MImageReplyBody.scss
@@ -25,6 +25,10 @@ limitations under the License.
 
 .mx_MImageReplyBody_thumbnail {
     grid-area: image;
+
+    .mx_MImageBody_thumbnail_container {
+        max-height: 44px !important;
+    }
 }
 
 .mx_MImageReplyBody_sender {

From dfe277b78d1e7fe39fe91245646b41d72bb367fc Mon Sep 17 00:00:00 2001
From: Tulir Asokan 
Date: Mon, 25 May 2020 19:24:03 +0300
Subject: [PATCH 0012/4306] Remove unnecessary right margin in reply blockquote

---
 res/css/views/elements/_ReplyThread.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyThread.scss
index d5388e4631..af8ca956ba 100644
--- a/res/css/views/elements/_ReplyThread.scss
+++ b/res/css/views/elements/_ReplyThread.scss
@@ -24,6 +24,7 @@ limitations under the License.
 
 blockquote.mx_ReplyThread {
     margin-left: 0;
+    margin-right: 0;
     margin-bottom: 8px;
     padding-left: 10px;
     border-left: 4px solid $button-bg-color;

From c60483728fc8526538e43c1a27834c85d8c1984c Mon Sep 17 00:00:00 2001
From: Tulir Asokan 
Date: Mon, 25 May 2020 19:33:30 +0300
Subject: [PATCH 0013/4306] Fix dispatcher import in ReplyTile.js

---
 src/components/views/rooms/ReplyTile.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/rooms/ReplyTile.js b/src/components/views/rooms/ReplyTile.js
index 34b2c6ad38..f6c4a69def 100644
--- a/src/components/views/rooms/ReplyTile.js
+++ b/src/components/views/rooms/ReplyTile.js
@@ -21,7 +21,7 @@ import { _t, _td } from '../../../languageHandler';
 
 import * as sdk from '../../../index';
 
-import dis from '../../../dispatcher';
+import dis from '../../../dispatcher/dispatcher';
 import SettingsStore from "../../../settings/SettingsStore";
 import {MatrixClient} from 'matrix-js-sdk';
 

From 53db386731422c88d4e24e299e9e56a150dad3cf Mon Sep 17 00:00:00 2001
From: Travis Ralston 
Date: Mon, 10 Aug 2020 22:06:30 -0600
Subject: [PATCH 0014/4306] Add support for blurhash (MSC2448)

MSC: https://github.com/matrix-org/matrix-doc/pull/2448

While the image loads, we can show a blurred version of it (calculated at upload time) so we don't have a blank space in the timeline.
---
 package.json                                  |  1 +
 src/ContentMessages.tsx                       | 10 +++-
 .../views/elements/BlurhashPlaceholder.tsx    | 56 +++++++++++++++++++
 src/components/views/messages/MImageBody.js   | 18 +++---
 yarn.lock                                     |  5 ++
 5 files changed, 81 insertions(+), 9 deletions(-)
 create mode 100644 src/components/views/elements/BlurhashPlaceholder.tsx

diff --git a/package.json b/package.json
index 548b33f353..989672d414 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,7 @@
     "@babel/runtime": "^7.10.5",
     "await-lock": "^2.0.1",
     "blueimp-canvas-to-blob": "^3.27.0",
+    "blurhash": "^1.1.3",
     "browser-encrypt-attachment": "^0.3.0",
     "browser-request": "^0.3.3",
     "classnames": "^2.2.6",
diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx
index 6f55a75d0c..7e57b34ff7 100644
--- a/src/ContentMessages.tsx
+++ b/src/ContentMessages.tsx
@@ -334,6 +334,7 @@ function uploadFile(matrixClient: MatrixClient, roomId: string, file: File | Blo
             if (file.type) {
                 encryptInfo.mimetype = file.type;
             }
+            // TODO: Blurhash for encrypted media?
             return {"file": encryptInfo};
         });
         (prom as IAbortablePromise).abort = () => {
@@ -344,11 +345,15 @@ function uploadFile(matrixClient: MatrixClient, roomId: string, file: File | Blo
     } else {
         const basePromise = matrixClient.uploadContent(file, {
             progressHandler: progressHandler,
+            onlyContentUri: false,
         });
-        const promise1 = basePromise.then(function(url) {
+        const promise1 = basePromise.then(function(body) {
             if (canceled) throw new UploadCanceledError();
             // If the attachment isn't encrypted then include the URL directly.
-            return {"url": url};
+            return {
+                "url": body.content_uri,
+                "blurhash": body["xyz.amorgan.blurhash"], // TODO: Use `body.blurhash` when MSC2448 lands
+            };
         });
         promise1.abort = () => {
             canceled = true;
@@ -550,6 +555,7 @@ export default class ContentMessages {
             return upload.promise.then(function(result) {
                 content.file = result.file;
                 content.url = result.url;
+                content.info['xyz.amorgan.blurhash'] = result.blurhash; // TODO: Use `blurhash` when MSC2448 lands
             });
         }).then(() => {
             // Await previous message being sent into the room
diff --git a/src/components/views/elements/BlurhashPlaceholder.tsx b/src/components/views/elements/BlurhashPlaceholder.tsx
new file mode 100644
index 0000000000..bed45bfe5a
--- /dev/null
+++ b/src/components/views/elements/BlurhashPlaceholder.tsx
@@ -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 React from 'react';
+import {decode} from "blurhash";
+
+interface IProps {
+    blurhash: string;
+    width: number;
+    height: number;
+}
+
+export default class BlurhashPlaceholder extends React.PureComponent {
+    private canvas: React.RefObject = React.createRef();
+
+    public componentDidMount() {
+        this.draw();
+    }
+
+    public componentDidUpdate() {
+        this.draw();
+    }
+
+    private draw() {
+        if (!this.canvas.current) return;
+
+        try {
+            const {width, height} = this.props;
+
+            const pixels = decode(this.props.blurhash, Math.ceil(width), Math.ceil(height));
+            const ctx = this.canvas.current.getContext("2d");
+            const imgData = ctx.createImageData(width, height);
+            imgData.data.set(pixels);
+            ctx.putImageData(imgData, 0, 0);
+        } catch (e) {
+            console.error("Error rendering blurhash: ", e);
+        }
+    }
+
+    public render() {
+        return ;
+    }
+}
diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js
index c92ae475bf..e0ac24c641 100644
--- a/src/components/views/messages/MImageBody.js
+++ b/src/components/views/messages/MImageBody.js
@@ -27,6 +27,7 @@ import { _t } from '../../../languageHandler';
 import SettingsStore from "../../../settings/SettingsStore";
 import MatrixClientContext from "../../../contexts/MatrixClientContext";
 import InlineSpinner from '../elements/InlineSpinner';
+import BlurhashPlaceholder from "../elements/BlurhashPlaceholder";
 
 export default class MImageBody extends React.Component {
     static propTypes = {
@@ -53,6 +54,8 @@ export default class MImageBody extends React.Component {
         this.onClick = this.onClick.bind(this);
         this._isGif = this._isGif.bind(this);
 
+        const imageInfo = this.props.mxEvent.getContent().info;
+
         this.state = {
             decryptedUrl: null,
             decryptedThumbnailUrl: null,
@@ -63,6 +66,7 @@ export default class MImageBody extends React.Component {
             loadedImageDimensions: null,
             hover: false,
             showImage: SettingsStore.getValue("showImages"),
+            blurhash: imageInfo ? imageInfo['xyz.amorgan.blurhash'] : null, // TODO: Use `blurhash` when MSC2448 lands.
         };
 
         this._image = createRef();
@@ -329,7 +333,8 @@ export default class MImageBody extends React.Component {
             infoWidth = content.info.w;
             infoHeight = content.info.h;
         } else {
-            // Whilst the image loads, display nothing.
+            // Whilst the image loads, display nothing. We also don't display a blurhash image
+            // because we don't really know what size of image we'll end up with.
             //
             // Once loaded, use the loaded image dimensions stored in `loadedImageDimensions`.
             //
@@ -368,8 +373,7 @@ export default class MImageBody extends React.Component {
         if (content.file !== undefined && this.state.decryptedUrl === null) {
             placeholder = ;
         } else if (!this.state.imgLoaded) {
-            // Deliberately, getSpinner is left unimplemented here, MStickerBody overides
-            placeholder = this.getPlaceholder();
+            placeholder = this.getPlaceholder(maxWidth, maxHeight);
         }
 
         let showPlaceholder = Boolean(placeholder);
@@ -391,7 +395,7 @@ export default class MImageBody extends React.Component {
 
         if (!this.state.showImage) {
             img = ;
-            showPlaceholder = false; // because we're hiding the image, so don't show the sticker icon.
+            showPlaceholder = false; // because we're hiding the image, so don't show the placeholder.
         }
 
         if (this._isGif() && !SettingsStore.getValue("autoplayGifsAndVideos") && !this.state.hover) {
@@ -433,9 +437,9 @@ export default class MImageBody extends React.Component {
     }
 
     // Overidden by MStickerBody
-    getPlaceholder() {
-        // MImageBody doesn't show a placeholder whilst the image loads, (but it could do)
-        return null;
+    getPlaceholder(width, height) {
+        if (!this.state.blurhash) return null;
+        return ;
     }
 
     // Overidden by MStickerBody
diff --git a/yarn.lock b/yarn.lock
index 98fe42ef13..f1cace67a1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2493,6 +2493,11 @@ blueimp-canvas-to-blob@^3.27.0:
   resolved "https://registry.yarnpkg.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.27.0.tgz#a2bd5c43587b95dedf0f6998603452d1bfcc9b9e"
   integrity sha512-AcIj+hCw6WquxzJuzC6KzgYmqxLFeTWe88KuY2BEIsW1zbEOfoinDAGlhyvFNGt+U3JElkVSK7anA1FaSdmmfA==
 
+blurhash@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.3.tgz#dc325af7da836d07a0861d830bdd63694382483e"
+  integrity sha512-yUhPJvXexbqbyijCIE/T2NCXcj9iNPhWmOKbPTuR/cm7Q5snXYIfnVnz6m7MWOXxODMz/Cr3UcVkRdHiuDVRDw==
+
 bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0:
   version "4.11.9"
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"

From 4394a20f87922e768852dd693b7d3eb8ef3f90f4 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 18 Aug 2020 09:56:38 +0200
Subject: [PATCH 0015/4306] setting added to User Settings -> Preferences ->
 Timeline as an opt out for users with german translation

---
 .../views/settings/tabs/user/PreferencesUserSettingsTab.js   | 1 +
 src/i18n/strings/de_DE.json                                  | 3 ++-
 src/i18n/strings/en_EN.json                                  | 1 +
 src/settings/Settings.ts                                     | 5 +++++
 4 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
index a77815a68c..6ed2fc2e39 100644
--- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
@@ -49,6 +49,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
         'showAvatarChanges',
         'showDisplaynameChanges',
         'showImages',
+        'dontShowChatEffects',
     ];
 
     static ADVANCED_SETTINGS = [
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 09dbcb2e18..edfe21d9d6 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2361,5 +2361,6 @@
     "%(brand)s encountered an error during upload of:": "%(brand)s hat einen Fehler festgestellt beim hochladen von:",
     "Use your account to sign in to the latest version of the app at ": "Verwende dein Konto um dich an der neusten Version der App anzumelden",
     "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!",
-    "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot"
+    "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot",
+    "Don't show chat effects": "Chat Effekte nicht zeigen"
 }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 974a96406f..98aee655fe 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -503,6 +503,7 @@
     "Manually verify all remote sessions": "Manually verify all remote sessions",
     "IRC display name width": "IRC display name width",
     "Enable experimental, compact IRC style layout": "Enable experimental, compact IRC style layout",
+    "Don't show chat effects": "Don't show chat effects",
     "Collecting app version information": "Collecting app version information",
     "Collecting logs": "Collecting logs",
     "Uploading report": "Uploading report",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index 714d80f983..59a3a4799b 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -586,4 +586,9 @@ export const SETTINGS: {[setting: string]: ISetting} = {
         displayName: _td("Enable experimental, compact IRC style layout"),
         default: false,
     },
+    "dontShowChatEffects": {
+        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
+        displayName: _td("Don't show chat effects"),
+        default: false,
+    },
 };

From ecd4d6e19ef58f6c0b99a94890a5cd82a53e7c2a Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 18 Aug 2020 17:57:51 +0200
Subject: [PATCH 0016/4306] test commit for confetti changes

---
 src/SlashCommands.tsx                     |  13 ++
 src/components/structures/RoomView.js     |   7 +-
 src/components/views/elements/Confetti.js | 209 ++++++++++++++++++++++
 src/i18n/strings/de_DE.json               |   3 +-
 src/i18n/strings/en_EN.json               |   1 +
 5 files changed, 230 insertions(+), 3 deletions(-)
 create mode 100644 src/components/views/elements/Confetti.js

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 2063ad3149..2d4d484899 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -44,6 +44,7 @@ import { ensureDMExists } from "./createRoom";
 import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
 import { Action } from "./dispatcher/actions";
 import { EffectiveMembership, getEffectiveMembership } from "./utils/membership";
+import {func} from "prop-types";
 
 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
 interface HTMLInputEvent extends Event {
@@ -1026,6 +1027,18 @@ export const Commands = [
         },
         category: CommandCategories.actions,
     }),
+    new Command({
+        command: "confetti",
+        description: _td("Throws confetti animation in the chat room"),
+        args: '/confetti + ',
+        runFn: function(roomId, args, command) {
+            return success((async () => {
+              const cli = MatrixClientPeg.get();
+              await cli.sendHtmlMessage(roomId, args);
+            })());
+        },
+        category: CommandCategories.messages,
+    }),
 
     // Command definitions for autocompletion ONLY:
     // /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 9a61523941..85cb1df848 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -57,6 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext";
 import { shieldStatusForRoom } from '../../utils/ShieldUtils';
 import {Action} from "../../dispatcher/actions";
 import {SettingLevel} from "../../settings/SettingLevel";
+import Confetti from "../views/elements/Confetti";
 
 const DEBUG = false;
 let debuglog = function() {};
@@ -67,7 +68,7 @@ if (DEBUG) {
     // using bind means that we get to keep useful line numbers in the console
     debuglog = console.log.bind(console);
 }
-
+let confetti;
 export default createReactClass({
     displayName: 'RoomView',
     propTypes: {
@@ -624,12 +625,14 @@ export default createReactClass({
             ev.preventDefault();
         }
     },
-
     onAction: function(payload) {
         switch (payload.action) {
             case 'message_send_failed':
             case 'message_sent':
                 this._checkIfAlone(this.state.room);
+                confetti = new Confetti('100', '100');
+                console.log('confetti sent');
+                confetti.animateConfetti('test', 'message');
                 break;
             case 'post_sticker_message':
               this.injectSticker(
diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js
new file mode 100644
index 0000000000..e9dc2c34c0
--- /dev/null
+++ b/src/components/views/elements/Confetti.js
@@ -0,0 +1,209 @@
+import React from "react";
+import SettingsStore from "../../../../lib/settings/SettingsStore";
+import PropTypes from "prop-types";
+
+export default class Confetti extends React.Component {
+    displayName: 'confetti';
+    constructor(props) {
+        super(props);
+        this.animateConfetti = this.animateConfetti.bind(this);
+        this.confetti.start = this.startConfetti;
+        this.startConfetti = this.startConfetti.bind(this);
+        this.confetti.stop = this.stopConfetti;
+        this.confetti.remove = this.removeConfetti;
+        this.confetti.isRunning = this.isConfettiRunning;
+    }
+   static propTypes = {
+        width: PropTypes.string.isRequired,
+        height: PropTypes.string.isRequired,
+    }
+    confetti = {
+        //set max confetti count
+        maxCount: 150,
+        //set the particle animation speed
+        speed: 3,
+        //the confetti animation frame interval in milliseconds
+        frameInterval: 15,
+        //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
+        alpha: 1.0,
+        start: null,
+    };
+    colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,",
+        "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,",
+        "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,",
+        "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
+    streamingConfetti = false;
+    animationTimer = null;
+    lastFrameTime = Date.now();
+    particles = [];
+    waveAngle = 0;
+    context = null;
+    supportsAnimationFrame = window.requestAnimationFrame ||
+        window.webkitRequestAnimationFrame ||
+        window.mozRequestAnimationFrame ||
+        window.oRequestAnimationFrame ||
+        window.msRequestAnimationFrame;
+
+    resetParticle(particle, width, height) {
+        particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")");
+        particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")");
+        particle.x = Math.random() * width;
+        particle.y = Math.random() * height - height;
+        particle.diameter = Math.random() * 10 + 5;
+        particle.tilt = Math.random() * 10 - 10;
+        particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
+        particle.tiltAngle = Math.random() * Math.PI;
+        return particle;
+    }
+
+    startConfetti(timeout) {
+        const width = window.innerWidth;
+        const height = window.innerHeight;
+        window.requestAnimationFrame = () => {
+            return window.requestAnimationFrame ||
+                window.webkitRequestAnimationFrame ||
+                window.mozRequestAnimationFrame ||
+                window.oRequestAnimationFrame ||
+                window.msRequestAnimationFrame ||
+                function(callback) {
+                    return window.setTimeout(callback, this.confetti.frameInterval);
+                };
+        };
+        let canvas = document.getElementById("confetti-canvas");
+        if (canvas === null) {
+            canvas = document.createElement("canvas");
+            canvas.setAttribute("id", "confetti-canvas");
+            canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
+            document.body.prepend(canvas);
+            canvas.width = width;
+            canvas.height = height;
+            window.addEventListener("resize", function () {
+                canvas.width = window.innerWidth;
+                canvas.height = window.innerHeight;
+            }, true);
+            this.context = canvas.getContext("2d");
+        } else if (this.context === null) {
+            this.context = canvas.getContext("2d");
+        }
+        const count = this.confetti.maxCount;
+        while (this.particles.length < count) {
+            this.particles.push(this.resetParticle({}, width, height));
+        }
+        this.streamingConfetti = true;
+        this.runAnimation();
+        if (timeout) {
+            window.setTimeout(this.stopConfetti, timeout);
+        }
+    }
+
+    stopConfetti() {
+        this.streamingConfetti = false;
+    }
+
+    runAnimation() {
+        if (this.particles.length === 0) {
+            this.context.clearRect(0, 0, window.innerWidth, window.innerHeight);
+            this.animationTimer = null;
+        } else {
+            const now = Date.now();
+            const delta = now - this.lastFrameTime;
+            if (!this.supportsAnimationFrame || delta > this.confetti.frameInterval) {
+                this.context.clearRect(0, 0, window.innerWidth, window.innerHeight);
+                this.updateParticles();
+                this.drawParticles(this.context);
+                this.lastFrameTime = now - (delta % this.confetti.frameInterval);
+            }
+            this.animationTimer = requestAnimationFrame(this.runAnimation);
+        }
+    }
+
+    removeConfetti() {
+        stop();
+        this.particles = [];
+    }
+
+    isConfettiRunning() {
+        return this.streamingConfetti;
+    }
+
+    drawParticles(context) {
+        let particle;
+        let x;
+        let x2;
+        let y2;
+        for (let i = 0; i < this.particles.length; i++) {
+            particle = this.particles[i];
+            context.beginPath();
+            context.lineWidth = particle.diameter;
+            x2 = particle.x + particle.tilt;
+            x = x2 + particle.diameter / 2;
+            y2 = particle.y + particle.tilt + particle.diameter / 2;
+            context.strokeStyle = particle.color;
+            context.moveTo(x, particle.y);
+            context.lineTo(x2, y2);
+            context.stroke();
+        }
+    }
+
+    updateParticles() {
+        const width = window.innerWidth;
+        const height = window.innerHeight;
+        let particle;
+        this.waveAngle += 0.01;
+        for (let i = 0; i < this.particles.length; i++) {
+            particle = this.particles[i];
+            if (!this.streamingConfetti && particle.y < -15) {
+                particle.y = height + 100;
+            } else {
+                particle.tiltAngle += particle.tiltAngleIncrement;
+                particle.x += Math.sin(this.waveAngle) - 0.5;
+                particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.confetti.speed) * 0.5;
+                particle.tilt = Math.sin(particle.tiltAngle) * 15;
+            }
+            if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
+                if (this.streamingConfetti && this.particles.length <= this.confetti.maxCount) {
+                    this.resetParticle(particle, width, height);
+                } else {
+                    this.particles.splice(i, 1);
+                    i--;
+                }
+            }
+        }
+    }
+
+    convertToHex(content) {
+        const contentBodyToHexArray = [];
+        let hex;
+        for (let i = 0; i < content.body.length; i++) {
+            hex = content.body.codePointAt(i).toString(16);
+            contentBodyToHexArray.push(hex);
+        }
+        return contentBodyToHexArray;
+    }
+
+    isChatEffectsDisabled() {
+        console.log('return value', SettingsStore.getValue('dontShowChatEffects'));
+        return SettingsStore.getValue('dontShowChatEffects');
+    }
+
+    isConfettiEmoji(content) {
+        const hexArray = this.convertToHex(content);
+        return !!(hexArray.includes('1f389') || hexArray.includes('1f38a'));
+    }
+
+     animateConfetti(userId, message) {
+        // const shortendUserId = userId.slice(1).split(":").slice(0, 1);
+         console.log('in animate confetti method');
+        if (!this.isChatEffectsDisabled()) {
+            this.confetti.start(3000);
+        }
+        if (!message) {
+            return ('*' + userId + ' throws confetti ');
+        }
+    }
+
+    render() {
+        return ( );
+    }
+}
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index edfe21d9d6..e4311c2111 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2362,5 +2362,6 @@
     "Use your account to sign in to the latest version of the app at ": "Verwende dein Konto um dich an der neusten Version der App anzumelden",
     "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!",
     "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot",
-    "Don't show chat effects": "Chat Effekte nicht zeigen"
+    "Don't show chat effects": "Chat Effekte nicht zeigen",
+    "Throws confetti animation in the chat room": "Throws confetti animation in the chat room"
 }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 98aee655fe..f09ec685ee 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -213,6 +213,7 @@
     "Thank you!": "Thank you!",
     "Opens chat with the given user": "Opens chat with the given user",
     "Sends a message to the given user": "Sends a message to the given user",
+    "Throws confetti animation in the chat room": "Throws confetti animation in the chat room",
     "Displays action": "Displays action",
     "Reason": "Reason",
     "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",

From 69227dd456bb9d7d78e16157dcabda2603345ae3 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 10:26:20 +0200
Subject: [PATCH 0017/4306] translations added

---
 src/i18n/strings/de_DE.json | 3 ++-
 src/i18n/strings/en_EN.json | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index e4311c2111..5e5639942b 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2363,5 +2363,6 @@
     "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!",
     "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot",
     "Don't show chat effects": "Chat Effekte nicht zeigen",
-    "Throws confetti animation in the chat room": "Throws confetti animation in the chat room"
+    "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti",
+    " sends confetti": " sendet Konfetti"
 }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f09ec685ee..efd68d06a6 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -213,7 +213,8 @@
     "Thank you!": "Thank you!",
     "Opens chat with the given user": "Opens chat with the given user",
     "Sends a message to the given user": "Sends a message to the given user",
-    "Throws confetti animation in the chat room": "Throws confetti animation in the chat room",
+    "Sends the given message with confetti": "Sends the given message with confetti",
+    " sends confetti": " sends confetti",
     "Displays action": "Displays action",
     "Reason": "Reason",
     "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",

From 34cee20140d4da373fc0be630da4e11709409ed9 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 10:43:41 +0200
Subject: [PATCH 0018/4306] added confetti on command /confetti

---
 src/SlashCommands.tsx | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 2d4d484899..8322512b73 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -45,6 +45,7 @@ import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
 import { Action } from "./dispatcher/actions";
 import { EffectiveMembership, getEffectiveMembership } from "./utils/membership";
 import {func} from "prop-types";
+import SettingsStore from "./settings/SettingsStore";
 
 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
 interface HTMLInputEvent extends Event {
@@ -1029,15 +1030,24 @@ export const Commands = [
     }),
     new Command({
         command: "confetti",
-        description: _td("Throws confetti animation in the chat room"),
-        args: '/confetti + ',
-        runFn: function(roomId, args, command) {
+        description: _td("Sends the given message with confetti"),
+        args: '',
+        runFn: function(roomId, args) {
             return success((async () => {
-              const cli = MatrixClientPeg.get();
-              await cli.sendHtmlMessage(roomId, args);
+                const cli = MatrixClientPeg.get();
+                const userId = cli.getUserId();
+                const userName = userId.slice(1).split(":").slice(0, 1);
+                const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects');
+                if (!args || isChatEffectsDisabled) {
+                    args = '*' + userName + _td(' sends confetti');
+                }
+                if (!isChatEffectsDisabled) {
+                    dis.dispatch({action: 'confetti'});
+                }
+                cli.sendHtmlMessage(roomId, args);
             })());
         },
-        category: CommandCategories.messages,
+        category: CommandCategories.actions,
     }),
 
     // Command definitions for autocompletion ONLY:

From a7567b2e31bb403a05490a299e7ca17fd595760c Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 10:44:32 +0200
Subject: [PATCH 0019/4306] confetti animationsd handeled on roomViewTimeline

---
 src/components/structures/RoomView.js | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 85cb1df848..e48063530d 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -57,7 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext";
 import { shieldStatusForRoom } from '../../utils/ShieldUtils';
 import {Action} from "../../dispatcher/actions";
 import {SettingLevel} from "../../settings/SettingLevel";
-import Confetti from "../views/elements/Confetti";
+import {animateConfetti, forceStopConfetti} from "../views/elements/Confetti";
 
 const DEBUG = false;
 let debuglog = function() {};
@@ -68,7 +68,6 @@ if (DEBUG) {
     // using bind means that we get to keep useful line numbers in the console
     debuglog = console.log.bind(console);
 }
-let confetti;
 export default createReactClass({
     displayName: 'RoomView',
     propTypes: {
@@ -511,6 +510,7 @@ export default createReactClass({
             this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
             this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
             this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
+            this.context.removeListener("Event.decrypted", this.onEventDecrypted);
         }
 
         window.removeEventListener('beforeunload', this.onPageUnload);
@@ -630,9 +630,9 @@ export default createReactClass({
             case 'message_send_failed':
             case 'message_sent':
                 this._checkIfAlone(this.state.room);
-                confetti = new Confetti('100', '100');
-                console.log('confetti sent');
-                confetti.animateConfetti('test', 'message');
+                break;
+            case 'confetti':
+                    animateConfetti(this._roomView.current.offsetWidth);
                 break;
             case 'post_sticker_message':
               this.injectSticker(
@@ -750,6 +750,18 @@ export default createReactClass({
                 });
             }
         }
+        if (!SettingsStore.getValue('dontShowChatEffects')) {
+            this.context.on('Event.decrypted', this.onEventDecrypted);
+        }
+    },
+     onEventDecrypted(ev) {
+         if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
+                     this.handleConfetti();
+     },
+    handleConfetti() {
+        if (this.context.isInitialSyncComplete()) {
+                dis.dispatch({action: 'confetti'});
+            }
     },
 
     onRoomName: function(room) {
@@ -786,6 +798,7 @@ export default createReactClass({
         this._calculateRecommendedVersion(room);
         this._updateE2EStatus(room);
         this._updatePermissions(room);
+        forceStopConfetti();
     },
 
     _calculateRecommendedVersion: async function(room) {

From 77de63bf4b06c5235e00c74673f2c0082a064195 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 10:44:49 +0200
Subject: [PATCH 0020/4306] confetti file added

---
 src/components/views/elements/Confetti.js | 252 +++++++++++-----------
 1 file changed, 121 insertions(+), 131 deletions(-)

diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js
index e9dc2c34c0..df2b004ce0 100644
--- a/src/components/views/elements/Confetti.js
+++ b/src/components/views/elements/Confetti.js
@@ -1,52 +1,48 @@
-import React from "react";
-import SettingsStore from "../../../../lib/settings/SettingsStore";
-import PropTypes from "prop-types";
+const confetti = {
+    //set max confetti count
+    maxCount: 150,
+    //syarn addet the particle animation speed
+    speed: 3,
+    //the confetti animation frame interval in milliseconds
+    frameInterval: 15,
+    //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
+    alpha: 1.0,
+    //call to start confetti animation (with optional timeout in milliseconds)
+    start: null,
+    //call to stop adding confetti
+    stop: null,
+    //call to stop the confetti animation and remove all confetti immediately
+    remove: null,
+    isRunning: null,
+    //call and returns true or false depending on whether the animation is running
+    animate: null,
+};
 
-export default class Confetti extends React.Component {
-    displayName: 'confetti';
-    constructor(props) {
-        super(props);
-        this.animateConfetti = this.animateConfetti.bind(this);
-        this.confetti.start = this.startConfetti;
-        this.startConfetti = this.startConfetti.bind(this);
-        this.confetti.stop = this.stopConfetti;
-        this.confetti.remove = this.removeConfetti;
-        this.confetti.isRunning = this.isConfettiRunning;
-    }
-   static propTypes = {
-        width: PropTypes.string.isRequired,
-        height: PropTypes.string.isRequired,
-    }
-    confetti = {
-        //set max confetti count
-        maxCount: 150,
-        //set the particle animation speed
-        speed: 3,
-        //the confetti animation frame interval in milliseconds
-        frameInterval: 15,
-        //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
-        alpha: 1.0,
-        start: null,
-    };
-    colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,",
-        "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,",
-        "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,",
-        "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
-    streamingConfetti = false;
-    animationTimer = null;
-    lastFrameTime = Date.now();
-    particles = [];
-    waveAngle = 0;
-    context = null;
-    supportsAnimationFrame = window.requestAnimationFrame ||
+(function() {
+    confetti.start = startConfetti;
+    confetti.stop = stopConfetti;
+    confetti.remove = removeConfetti;
+    confetti.isRunning = isConfettiRunning;
+    confetti.animate = animateConfetti;
+    const supportsAnimationFrame = window.requestAnimationFrame ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame ||
         window.oRequestAnimationFrame ||
         window.msRequestAnimationFrame;
+    const colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,",
+        "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,",
+        "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,",
+        "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
+    let streamingConfetti = false;
+    let animationTimer = null;
+    let lastFrameTime = Date.now();
+    let particles = [];
+    let waveAngle = 0;
+    let context = null;
 
-    resetParticle(particle, width, height) {
-        particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")");
-        particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")");
+    function resetParticle(particle, width, height) {
+        particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
+        particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
         particle.x = Math.random() * width;
         particle.y = Math.random() * height - height;
         particle.diameter = Math.random() * 10 + 5;
@@ -56,154 +52,148 @@ export default class Confetti extends React.Component {
         return particle;
     }
 
-    startConfetti(timeout) {
-        const width = window.innerWidth;
+    function runAnimation() {
+        if (particles.length === 0) {
+            context.clearRect(0, 0, window.innerWidth, window.innerHeight);
+            animationTimer = null;
+        } else {
+            const now = Date.now();
+            const delta = now - lastFrameTime;
+            if (!supportsAnimationFrame || delta > confetti.frameInterval) {
+                context.clearRect(0, 0, window.innerWidth, window.innerHeight);
+                updateParticles();
+                drawParticles(context);
+                lastFrameTime = now - (delta % confetti.frameInterval);
+            }
+            animationTimer = requestAnimationFrame(runAnimation);
+        }
+    }
+
+    function startConfetti(roomWidth, timeout) {
+        const width = roomWidth;
         const height = window.innerHeight;
-        window.requestAnimationFrame = () => {
+        window.requestAnimationFrame = (function () {
             return window.requestAnimationFrame ||
                 window.webkitRequestAnimationFrame ||
                 window.mozRequestAnimationFrame ||
                 window.oRequestAnimationFrame ||
                 window.msRequestAnimationFrame ||
-                function(callback) {
-                    return window.setTimeout(callback, this.confetti.frameInterval);
+                function (callback) {
+                    return window.setTimeout(callback, confetti.frameInterval);
                 };
-        };
+        })();
         let canvas = document.getElementById("confetti-canvas");
         if (canvas === null) {
             canvas = document.createElement("canvas");
             canvas.setAttribute("id", "confetti-canvas");
-            canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
+            canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0");
             document.body.prepend(canvas);
             canvas.width = width;
             canvas.height = height;
-            window.addEventListener("resize", function () {
-                canvas.width = window.innerWidth;
+            window.addEventListener("resize", function() {
+                canvas.width = roomWidth;
                 canvas.height = window.innerHeight;
             }, true);
-            this.context = canvas.getContext("2d");
-        } else if (this.context === null) {
-            this.context = canvas.getContext("2d");
+            context = canvas.getContext("2d");
+        } else if (context === null) {
+            context = canvas.getContext("2d");
         }
-        const count = this.confetti.maxCount;
-        while (this.particles.length < count) {
-            this.particles.push(this.resetParticle({}, width, height));
+        const count = confetti.maxCount;
+        while (particles.length < count) {
+            particles.push(resetParticle({}, width, height));
         }
-        this.streamingConfetti = true;
-        this.runAnimation();
+        streamingConfetti = true;
+        runAnimation();
         if (timeout) {
-            window.setTimeout(this.stopConfetti, timeout);
+            window.setTimeout(stopConfetti, timeout);
         }
     }
 
-    stopConfetti() {
-        this.streamingConfetti = false;
+    function stopConfetti() {
+        streamingConfetti = false;
     }
 
-    runAnimation() {
-        if (this.particles.length === 0) {
-            this.context.clearRect(0, 0, window.innerWidth, window.innerHeight);
-            this.animationTimer = null;
-        } else {
-            const now = Date.now();
-            const delta = now - this.lastFrameTime;
-            if (!this.supportsAnimationFrame || delta > this.confetti.frameInterval) {
-                this.context.clearRect(0, 0, window.innerWidth, window.innerHeight);
-                this.updateParticles();
-                this.drawParticles(this.context);
-                this.lastFrameTime = now - (delta % this.confetti.frameInterval);
-            }
-            this.animationTimer = requestAnimationFrame(this.runAnimation);
-        }
-    }
-
-    removeConfetti() {
+    function removeConfetti() {
         stop();
-        this.particles = [];
+        particles = [];
     }
 
-    isConfettiRunning() {
-        return this.streamingConfetti;
+    function isConfettiRunning() {
+        return streamingConfetti;
     }
 
-    drawParticles(context) {
+    function drawParticles(context) {
         let particle;
-        let x;
-        let x2;
-        let y2;
-        for (let i = 0; i < this.particles.length; i++) {
-            particle = this.particles[i];
+        let x; let x2; let y2;
+        for (let i = 0; i < particles.length; i++) {
+            particle = particles[i];
             context.beginPath();
             context.lineWidth = particle.diameter;
             x2 = particle.x + particle.tilt;
             x = x2 + particle.diameter / 2;
             y2 = particle.y + particle.tilt + particle.diameter / 2;
-            context.strokeStyle = particle.color;
+            if (confetti.gradient) {
+                const gradient = context.createLinearGradient(x, particle.y, x2, y2);
+                gradient.addColorStop("0", particle.color);
+                gradient.addColorStop("1.0", particle.color2);
+                context.strokeStyle = gradient;
+            } else {
+                context.strokeStyle = particle.color;
+            }
             context.moveTo(x, particle.y);
             context.lineTo(x2, y2);
             context.stroke();
         }
     }
 
-    updateParticles() {
+    function updateParticles() {
         const width = window.innerWidth;
         const height = window.innerHeight;
         let particle;
-        this.waveAngle += 0.01;
-        for (let i = 0; i < this.particles.length; i++) {
-            particle = this.particles[i];
-            if (!this.streamingConfetti && particle.y < -15) {
+        waveAngle += 0.01;
+        for (let i = 0; i < particles.length; i++) {
+            particle = particles[i];
+            if (!streamingConfetti && particle.y < -15) {
                 particle.y = height + 100;
             } else {
                 particle.tiltAngle += particle.tiltAngleIncrement;
-                particle.x += Math.sin(this.waveAngle) - 0.5;
-                particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.confetti.speed) * 0.5;
+                particle.x += Math.sin(waveAngle) - 0.5;
+                particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
                 particle.tilt = Math.sin(particle.tiltAngle) * 15;
             }
             if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
-                if (this.streamingConfetti && this.particles.length <= this.confetti.maxCount) {
-                    this.resetParticle(particle, width, height);
+                if (streamingConfetti && particles.length <= confetti.maxCount) {
+                    resetParticle(particle, width, height);
                 } else {
-                    this.particles.splice(i, 1);
+                    particles.splice(i, 1);
                     i--;
                 }
             }
         }
     }
+})();
 
-    convertToHex(content) {
-        const contentBodyToHexArray = [];
-        let hex;
+export function convertToHex(content) {
+    const contentBodyToHexArray = [];
+    let hex;
+    if (content.body) {
         for (let i = 0; i < content.body.length; i++) {
             hex = content.body.codePointAt(i).toString(16);
             contentBodyToHexArray.push(hex);
         }
-        return contentBodyToHexArray;
-    }
-
-    isChatEffectsDisabled() {
-        console.log('return value', SettingsStore.getValue('dontShowChatEffects'));
-        return SettingsStore.getValue('dontShowChatEffects');
-    }
-
-    isConfettiEmoji(content) {
-        const hexArray = this.convertToHex(content);
-        return !!(hexArray.includes('1f389') || hexArray.includes('1f38a'));
-    }
-
-     animateConfetti(userId, message) {
-        // const shortendUserId = userId.slice(1).split(":").slice(0, 1);
-         console.log('in animate confetti method');
-        if (!this.isChatEffectsDisabled()) {
-            this.confetti.start(3000);
-        }
-        if (!message) {
-            return ('*' + userId + ' throws confetti ');
-        }
-    }
-
-    render() {
-        return ( );
     }
+    return contentBodyToHexArray;
+}
+
+export function isConfettiEmoji(content) {
+    const hexArray = convertToHex(content);
+    return !!(hexArray.includes('1f389') || hexArray.includes('1f38a'));
+}
+
+export function animateConfetti(roomWidth) {
+        confetti.start(roomWidth, 3000);
+}
+export function forceStopConfetti() {
+    console.log('confetti should stop');
+    confetti.remove();
 }

From 03b2a529ef681e0e0777af57418f74ebba458954 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 11:29:46 +0200
Subject: [PATCH 0021/4306] remove unused var

---
 src/components/views/elements/Confetti.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js
index df2b004ce0..371c26a4b5 100644
--- a/src/components/views/elements/Confetti.js
+++ b/src/components/views/elements/Confetti.js
@@ -34,7 +34,7 @@ const confetti = {
         "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,",
         "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
     let streamingConfetti = false;
-    let animationTimer = null;
+   // let animationTimer = null;
     let lastFrameTime = Date.now();
     let particles = [];
     let waveAngle = 0;
@@ -55,7 +55,7 @@ const confetti = {
     function runAnimation() {
         if (particles.length === 0) {
             context.clearRect(0, 0, window.innerWidth, window.innerHeight);
-            animationTimer = null;
+            //animationTimer = null;
         } else {
             const now = Date.now();
             const delta = now - lastFrameTime;
@@ -65,7 +65,7 @@ const confetti = {
                 drawParticles(context);
                 lastFrameTime = now - (delta % confetti.frameInterval);
             }
-            animationTimer = requestAnimationFrame(runAnimation);
+           requestAnimationFrame(runAnimation);
         }
     }
 

From 2a8b1e0ccd58628214a496b0b25f18ad96755997 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 11:55:23 +0200
Subject: [PATCH 0022/4306] remove space before function parentheses and
 maximum allowed line

---
 src/components/views/elements/Confetti.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js
index 371c26a4b5..7d4faa3a17 100644
--- a/src/components/views/elements/Confetti.js
+++ b/src/components/views/elements/Confetti.js
@@ -72,13 +72,13 @@ const confetti = {
     function startConfetti(roomWidth, timeout) {
         const width = roomWidth;
         const height = window.innerHeight;
-        window.requestAnimationFrame = (function () {
+        window.requestAnimationFrame = (function() {
             return window.requestAnimationFrame ||
                 window.webkitRequestAnimationFrame ||
                 window.mozRequestAnimationFrame ||
                 window.oRequestAnimationFrame ||
                 window.msRequestAnimationFrame ||
-                function (callback) {
+                function(callback) {
                     return window.setTimeout(callback, confetti.frameInterval);
                 };
         })();
@@ -86,7 +86,8 @@ const confetti = {
         if (canvas === null) {
             canvas = document.createElement("canvas");
             canvas.setAttribute("id", "confetti-canvas");
-            canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0");
+            canvas.setAttribute("style",
+                "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0");
             document.body.prepend(canvas);
             canvas.width = width;
             canvas.height = height;

From b79cf1e7ad00cd06ae0b38c8b37612877ec59481 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 13:59:11 +0200
Subject: [PATCH 0023/4306] updated translated string

---
 src/SlashCommands.tsx       | 2 +-
 src/i18n/strings/de_DE.json | 1 -
 src/i18n/strings/en_EN.json | 2 +-
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 8322512b73..ba0aea73f0 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -1039,7 +1039,7 @@ export const Commands = [
                 const userName = userId.slice(1).split(":").slice(0, 1);
                 const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects');
                 if (!args || isChatEffectsDisabled) {
-                    args = '*' + userName + _td(' sends confetti');
+                    args = _t("* %(userName)s sends confetti", {userName});
                 }
                 if (!isChatEffectsDisabled) {
                     dis.dispatch({action: 'confetti'});
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 5e5639942b..46ce139e6e 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2364,5 +2364,4 @@
     "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot",
     "Don't show chat effects": "Chat Effekte nicht zeigen",
     "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti",
-    " sends confetti": " sendet Konfetti"
 }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index efd68d06a6..78a2d51c56 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -214,7 +214,7 @@
     "Opens chat with the given user": "Opens chat with the given user",
     "Sends a message to the given user": "Sends a message to the given user",
     "Sends the given message with confetti": "Sends the given message with confetti",
-    " sends confetti": " sends confetti",
+    "* %(userName)s sends confetti": "* %(userName)s sends confetti",
     "Displays action": "Displays action",
     "Reason": "Reason",
     "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",

From 5b7ccb5a7837e134d28795b7cb8ddc68716ca7c2 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 14:04:20 +0200
Subject: [PATCH 0024/4306] fix indentation spaces and readability line spaces

---
 src/components/structures/RoomView.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index e48063530d..240d300751 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -68,6 +68,7 @@ if (DEBUG) {
     // using bind means that we get to keep useful line numbers in the console
     debuglog = console.log.bind(console);
 }
+
 export default createReactClass({
     displayName: 'RoomView',
     propTypes: {
@@ -624,6 +625,7 @@ export default createReactClass({
             ev.stopPropagation();
             ev.preventDefault();
         }
+
     },
     onAction: function(payload) {
         switch (payload.action) {
@@ -632,7 +634,7 @@ export default createReactClass({
                 this._checkIfAlone(this.state.room);
                 break;
             case 'confetti':
-                    animateConfetti(this._roomView.current.offsetWidth);
+                animateConfetti(this._roomView.current.offsetWidth);
                 break;
             case 'post_sticker_message':
               this.injectSticker(
@@ -756,12 +758,12 @@ export default createReactClass({
     },
      onEventDecrypted(ev) {
          if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
-                     this.handleConfetti();
+         this.handleConfetti();
      },
     handleConfetti() {
         if (this.context.isInitialSyncComplete()) {
-                dis.dispatch({action: 'confetti'});
-            }
+            dis.dispatch({action: 'confetti'});
+        }
     },
 
     onRoomName: function(room) {

From f1c7139711f87dd818f9143fc6ec032e9ce41509 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Mon, 24 Aug 2020 14:25:06 +0200
Subject: [PATCH 0025/4306] remove not needed comma

---
 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 46ce139e6e..b5a69d7e72 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2363,5 +2363,5 @@
     "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!",
     "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot",
     "Don't show chat effects": "Chat Effekte nicht zeigen",
-    "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti",
+    "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti"
 }

From eef654e0e3189a8fcba03c8463d62ecbe2a3e745 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 11:09:10 +0200
Subject: [PATCH 0026/4306] fix indentation and remove console.log

---
 src/components/views/elements/Confetti.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js
index 7d4faa3a17..b0f88dedb7 100644
--- a/src/components/views/elements/Confetti.js
+++ b/src/components/views/elements/Confetti.js
@@ -192,9 +192,8 @@ export function isConfettiEmoji(content) {
 }
 
 export function animateConfetti(roomWidth) {
-        confetti.start(roomWidth, 3000);
+    confetti.start(roomWidth, 3000);
 }
 export function forceStopConfetti() {
-    console.log('confetti should stop');
     confetti.remove();
 }

From d41ffb1b4be9eeff5330c9e3ca5891cf22bb7f46 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 11:09:57 +0200
Subject: [PATCH 0027/4306] pass ev to handleConfetti in order to check content
 before dispatch

---
 src/components/structures/RoomView.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 240d300751..b24d6efa2a 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -57,7 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext";
 import { shieldStatusForRoom } from '../../utils/ShieldUtils';
 import {Action} from "../../dispatcher/actions";
 import {SettingLevel} from "../../settings/SettingLevel";
-import {animateConfetti, forceStopConfetti} from "../views/elements/Confetti";
+import {animateConfetti, forceStopConfetti, isConfettiEmoji} from "../views/elements/Confetti";
 
 const DEBUG = false;
 let debuglog = function() {};
@@ -758,11 +758,13 @@ export default createReactClass({
     },
      onEventDecrypted(ev) {
          if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
-         this.handleConfetti();
+         this.handleConfetti(ev);
      },
-    handleConfetti() {
+    handleConfetti(ev) {
         if (this.context.isInitialSyncComplete()) {
-            dis.dispatch({action: 'confetti'});
+            if (isConfettiEmoji(ev.getContent())) {
+                dis.dispatch({action: 'confetti'});
+            }
         }
     },
 

From 43f266bfe333c2b6c5ece0be86ea5089e5e11c80 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 11:11:20 +0200
Subject: [PATCH 0028/4306] remove unused import fix if condition  trying (pass
 the dispatcher to sendHtmlMessage)

---
 src/SlashCommands.tsx | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index ba0aea73f0..6b321ce092 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -44,7 +44,6 @@ import { ensureDMExists } from "./createRoom";
 import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
 import { Action } from "./dispatcher/actions";
 import { EffectiveMembership, getEffectiveMembership } from "./utils/membership";
-import {func} from "prop-types";
 import SettingsStore from "./settings/SettingsStore";
 
 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
@@ -1038,13 +1037,11 @@ export const Commands = [
                 const userId = cli.getUserId();
                 const userName = userId.slice(1).split(":").slice(0, 1);
                 const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects');
-                if (!args || isChatEffectsDisabled) {
+                if ((!args) || (!args && isChatEffectsDisabled)) {
                     args = _t("* %(userName)s sends confetti", {userName});
                 }
-                if (!isChatEffectsDisabled) {
-                    dis.dispatch({action: 'confetti'});
-                }
-                cli.sendHtmlMessage(roomId, args);
+                cli.sendHtmlMessage(roomId, args,
+                    dis.dispatch({action: 'confetti'}));
             })());
         },
         category: CommandCategories.actions,

From cc71531493df1e5e095b7f173909cbb4606a4f16 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 13:36:04 +0200
Subject: [PATCH 0029/4306] reverted German language translations

---
 src/i18n/strings/de_DE.json | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 2cea1519df..3d5ba3722e 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -2409,6 +2409,4 @@
     "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Wenn du jetzt abbrichst, kannst du verschlüsselte Nachrichten und Daten verlieren, wenn du den Zugriff auf deine Logins verlierst.",
     "You can also set up Secure Backup & manage your keys in Settings.": "Du kannst auch in den Einstellungen eine Sicherung erstellen & deine Schlüssel verwalten.",
     "Set up Secure backup": "Sicheres Backup einrichten"
-    "Don't show chat effects": "Chat Effekte nicht zeigen",
-    "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti"
 }

From 4527755f7e2df6f3b6622f1cd740469df608d587 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 16:18:01 +0200
Subject: [PATCH 0030/4306] updated translation

---
 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 029551eb34..223e063762 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -210,7 +210,7 @@
     "Opens chat with the given user": "Opens chat with the given user",
     "Sends a message to the given user": "Sends a message to the given user",
     "Sends the given message with confetti": "Sends the given message with confetti",
-    "* %(userName)s sends confetti": "* %(userName)s sends confetti",
+    "sends confetti": "sends confetti",
     "Displays action": "Displays action",
     "Reason": "Reason",
     "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",

From 5753c964317ab20b1682874416d00cdf9e6c5820 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Tue, 25 Aug 2020 16:39:57 +0200
Subject: [PATCH 0031/4306] a workaround to make ocnfetti work on recipient
 side. changed the implementation of on.Event.decrypted function

---
 src/SlashCommands.tsx                 | 13 +++++++------
 src/components/structures/RoomView.js | 13 ++++++-------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index abd4f5449b..03aec46e46 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -1029,15 +1029,16 @@ export const Commands = [
         args: '',
         runFn: function(roomId, args) {
             return success((async () => {
-                const cli = MatrixClientPeg.get();
-                const userId = cli.getUserId();
-                const userName = userId.slice(1).split(":").slice(0, 1);
                 const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects');
                 if ((!args) || (!args && isChatEffectsDisabled)) {
-                    args = _t("* %(userName)s sends confetti", {userName});
+                    args = _t("sends confetti");
+                    MatrixClientPeg.get().sendEmoteMessage(roomId, args);
+                } else {
+                    MatrixClientPeg.get().sendHtmlMessage(roomId, args);
+                }
+                if (!isChatEffectsDisabled) {
+                    dis.dispatch({action: 'confetti'});
                 }
-                cli.sendHtmlMessage(roomId, args,
-                    dis.dispatch({action: 'confetti'}));
             })());
         },
         category: CommandCategories.actions,
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index dfc92526c7..d5ccbf1c8c 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -511,7 +511,6 @@ export default createReactClass({
             this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
             this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
             this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
-            this.context.removeListener("Event.decrypted", this.onEventDecrypted);
         }
 
         window.removeEventListener('beforeunload', this.onPageUnload);
@@ -753,16 +752,16 @@ export default createReactClass({
             }
         }
         if (!SettingsStore.getValue('dontShowChatEffects')) {
-            this.context.on('Event.decrypted', this.onEventDecrypted);
+            this.context.on("Event.decrypted", (ev) => {
+                if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
+                this.handleConfetti(ev);
+            });
         }
     },
-     onEventDecrypted(ev) {
-         if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
-         this.handleConfetti(ev);
-     },
     handleConfetti(ev) {
         if (this.context.isInitialSyncComplete()) {
-            if (isConfettiEmoji(ev.getContent())) {
+            const messageBody = _t('sends confetti');
+            if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) {
                 dis.dispatch({action: 'confetti'});
             }
         }

From 95051a42b1f2755f52a980ef4521edc88ab728b0 Mon Sep 17 00:00:00 2001
From: nurjinn jafar 
Date: Wed, 26 Aug 2020 18:56:23 +0200
Subject: [PATCH 0032/4306] checking for unreadMessages before sending confetti
 throwing the confetti on the sender's side change sendHtmlMessage to
 sendTextMessage in slashCommands

---
 src/SlashCommands.tsx                           |  2 +-
 src/components/structures/RoomView.js           | 17 ++++++++++-------
 .../views/rooms/SendMessageComposer.js          |  7 +++++++
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 03aec46e46..28eaa8123b 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -1034,7 +1034,7 @@ export const Commands = [
                     args = _t("sends confetti");
                     MatrixClientPeg.get().sendEmoteMessage(roomId, args);
                 } else {
-                    MatrixClientPeg.get().sendHtmlMessage(roomId, args);
+                    MatrixClientPeg.get().sendTextMessage(roomId, args);
                 }
                 if (!isChatEffectsDisabled) {
                     dis.dispatch({action: 'confetti'});
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index d5ccbf1c8c..92f43c75ca 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -189,6 +189,7 @@ export default createReactClass({
         this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
         this.context.on("userTrustStatusChanged", this.onUserVerificationChanged);
         this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
+        this.context.on("Event.decrypted", this.onEventDecrypted);
         // Start listening for RoomViewStore updates
         this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
         this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
@@ -511,6 +512,7 @@ export default createReactClass({
             this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
             this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
             this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
+            this.context.removeListener("Event.decrypted", this.onEventDecrypted);
         }
 
         window.removeEventListener('beforeunload', this.onPageUnload);
@@ -751,15 +753,16 @@ export default createReactClass({
                 });
             }
         }
-        if (!SettingsStore.getValue('dontShowChatEffects')) {
-            this.context.on("Event.decrypted", (ev) => {
-                if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
-                this.handleConfetti(ev);
-            });
-        }
+    },
+    onEventDecrypted(ev) {
+    if (!SettingsStore.getValue('dontShowChatEffects')) {
+        if (ev.isBeingDecrypted() || ev.isDecryptionFailure() ||
+            this.state.room.getUnreadNotificationCount() === 0) return;
+        this.handleConfetti(ev);
+    }
     },
     handleConfetti(ev) {
-        if (this.context.isInitialSyncComplete()) {
+        if (this.state.matrixClientIsReady) {
             const messageBody = _t('sends confetti');
             if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) {
                 dis.dispatch({action: 'confetti'});
diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js
index 6a7b2fc753..0b873a9bab 100644
--- a/src/components/views/rooms/SendMessageComposer.js
+++ b/src/components/views/rooms/SendMessageComposer.js
@@ -44,6 +44,8 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
 import {MatrixClientPeg} from "../../../MatrixClientPeg";
 import RateLimitedFunc from '../../../ratelimitedfunc';
 import {Action} from "../../../dispatcher/actions";
+import {isConfettiEmoji} from "../elements/Confetti";
+import SettingsStore from "../../../settings/SettingsStore";
 
 function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
     const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
@@ -313,6 +315,11 @@ export default class SendMessageComposer extends React.Component {
                 });
             }
             dis.dispatch({action: "message_sent"});
+            if (!SettingsStore.getValue('dontShowChatEffects')) {
+                if (isConfettiEmoji(content)) {
+                dis.dispatch({action: 'confetti'});
+                }
+            }
         }
 
         this.sendHistoryManager.save(this.model);

From db61d343f5f9f2dc8552b97d9243c0ebfe8baa94 Mon Sep 17 00:00:00 2001
From: Clemens Zeidler 
Date: Sun, 30 Aug 2020 20:17:08 +1200
Subject: [PATCH 0033/4306] Add option to send/edit a message with Ctrl + Enter
 / Command + Enter

When editing multi-line text this option helps to prevent accidentally
sending a message too early. With this option, Enter just inserts a new
line.

For example, composing programming code in a dev chat becomes much
easier when Enter just inserts a new line instead of sending the
message.

Signed-off-by: Clemens Zeidler 
---
 src/components/views/rooms/EditMessageComposer.js         | 8 ++++++--
 src/components/views/rooms/SendMessageComposer.js         | 8 ++++++--
 .../settings/tabs/user/PreferencesUserSettingsTab.js      | 1 +
 src/i18n/strings/en_EN.json                               | 1 +
 src/settings/Settings.ts                                  | 5 +++++
 5 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js
index 78c7de887d..636c5b27ff 100644
--- a/src/components/views/rooms/EditMessageComposer.js
+++ b/src/components/views/rooms/EditMessageComposer.js
@@ -29,9 +29,10 @@ import EditorStateTransfer from '../../../utils/EditorStateTransfer';
 import classNames from 'classnames';
 import {EventStatus} from 'matrix-js-sdk';
 import BasicMessageComposer from "./BasicMessageComposer";
-import {Key} from "../../../Keyboard";
+import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard";
 import MatrixClientContext from "../../../contexts/MatrixClientContext";
 import {Action} from "../../../dispatcher/actions";
+import SettingsStore from "../../../settings/SettingsStore";
 
 function _isReply(mxEvent) {
     const relatesTo = mxEvent.getContent()["m.relates_to"];
@@ -135,7 +136,10 @@ export default class EditMessageComposer extends React.Component {
         if (event.metaKey || event.altKey || event.shiftKey) {
             return;
         }
-        if (event.key === Key.ENTER) {
+        const ctrlEnterToSend = !!SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend');
+        const send = ctrlEnterToSend ? event.key === Key.ENTER && isOnlyCtrlOrCmdKeyEvent(event)
+            : event.key === Key.ENTER;
+        if (send) {
             this._sendEdit();
             event.preventDefault();
         } else if (event.key === Key.ESCAPE) {
diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js
index 25dcf8ccd5..dd1b67c989 100644
--- a/src/components/views/rooms/SendMessageComposer.js
+++ b/src/components/views/rooms/SendMessageComposer.js
@@ -39,11 +39,12 @@ import * as sdk from '../../../index';
 import Modal from '../../../Modal';
 import {_t, _td} from '../../../languageHandler';
 import ContentMessages from '../../../ContentMessages';
-import {Key} from "../../../Keyboard";
+import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard";
 import MatrixClientContext from "../../../contexts/MatrixClientContext";
 import {MatrixClientPeg} from "../../../MatrixClientPeg";
 import RateLimitedFunc from '../../../ratelimitedfunc';
 import {Action} from "../../../dispatcher/actions";
+import SettingsStore from "../../../settings/SettingsStore";
 
 function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
     const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
@@ -122,7 +123,10 @@ export default class SendMessageComposer extends React.Component {
             return;
         }
         const hasModifier = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
-        if (event.key === Key.ENTER && !hasModifier) {
+        const ctrlEnterToSend = !!SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend');
+        const send = ctrlEnterToSend ? event.key === Key.ENTER && isOnlyCtrlOrCmdKeyEvent(event)
+            : event.key === Key.ENTER && !hasModifier;
+        if (send) {
             this._sendMessage();
             event.preventDefault();
         } else if (event.key === Key.ARROW_UP) {
diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
index a77815a68c..64208cb8cd 100644
--- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
@@ -33,6 +33,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
         'MessageComposerInput.autoReplaceEmoji',
         'MessageComposerInput.suggestEmoji',
         'sendTypingNotifications',
+        'MessageComposerInput.ctrlEnterToSend',
     ];
 
     static TIMELINE_SETTINGS = [
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 47063bdae4..277d9c5952 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -477,6 +477,7 @@
     "Enable big emoji in chat": "Enable big emoji in chat",
     "Send typing notifications": "Send typing notifications",
     "Show typing notifications": "Show typing notifications",
+    "Use Ctrl + Enter to send a message (Mac: Command + Enter)": "Use Ctrl + Enter to send a message (Mac: Command + Enter)",
     "Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
     "Mirror local video feed": "Mirror local video feed",
     "Enable Community Filter Panel": "Enable Community Filter Panel",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index 95861e11df..d2d268b2bb 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -321,6 +321,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
         displayName: _td("Show typing notifications"),
         default: true,
     },
+    "MessageComposerInput.ctrlEnterToSend": {
+        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
+        displayName: _td("Use Ctrl + Enter to send a message (Mac: Command + Enter)"),
+        default: false,
+    },
     "MessageComposerInput.autoReplaceEmoji": {
         supportedLevels: LEVELS_ACCOUNT_SETTINGS,
         displayName: _td('Automatically replace plain text Emoji'),

From 9031c58aebd08b7c6ab07e173f9895006491af5c Mon Sep 17 00:00:00 2001
From: Clemens Zeidler 
Date: Tue, 8 Sep 2020 21:46:09 +1200
Subject: [PATCH 0034/4306] Make settings label platform specific

---
 src/i18n/strings/en_EN.json | 3 ++-
 src/settings/Settings.ts    | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 277d9c5952..a66478ddc9 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -477,7 +477,8 @@
     "Enable big emoji in chat": "Enable big emoji in chat",
     "Send typing notifications": "Send typing notifications",
     "Show typing notifications": "Show typing notifications",
-    "Use Ctrl + Enter to send a message (Mac: Command + Enter)": "Use Ctrl + Enter to send a message (Mac: Command + Enter)",
+    "Use Command + Enter to send a message": "Use Command + Enter to send a message",
+    "Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
     "Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
     "Mirror local video feed": "Mirror local video feed",
     "Enable Community Filter Panel": "Enable Community Filter Panel",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index d2d268b2bb..afe9a50c1e 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -32,6 +32,7 @@ import UseSystemFontController from './controllers/UseSystemFontController';
 import { SettingLevel } from "./SettingLevel";
 import SettingController from "./controllers/SettingController";
 import { RightPanelPhases } from "../stores/RightPanelStorePhases";
+import { isMac } from '../Keyboard';
 
 // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
 const LEVELS_ROOM_SETTINGS = [
@@ -323,7 +324,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
     },
     "MessageComposerInput.ctrlEnterToSend": {
         supportedLevels: LEVELS_ACCOUNT_SETTINGS,
-        displayName: _td("Use Ctrl + Enter to send a message (Mac: Command + Enter)"),
+        displayName: isMac ? _td("Use Command + Enter to send a message") : _td("Use Ctrl + Enter to send a message"),
         default: false,
     },
     "MessageComposerInput.autoReplaceEmoji": {

From 0604c86779cdea98ace30bdd78eb4db6888ffc40 Mon Sep 17 00:00:00 2001
From: Aleks Kissinger 
Date: Sat, 19 Sep 2020 15:30:00 +0100
Subject: [PATCH 0035/4306] added katex package and import

---
 package.json      | 1 +
 src/HtmlUtils.tsx | 1 +
 yarn.lock         | 7 +++++++
 3 files changed, 9 insertions(+)

diff --git a/package.json b/package.json
index 156cbb1bc8..7aa3df136b 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
     "highlight.js": "^10.1.2",
     "html-entities": "^1.3.1",
     "is-ip": "^2.0.0",
+    "katex": "^0.12.0",
     "linkifyjs": "^2.1.9",
     "lodash": "^4.17.19",
     "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx
index bd314c2e5f..99acbfcb0c 100644
--- a/src/HtmlUtils.tsx
+++ b/src/HtmlUtils.tsx
@@ -26,6 +26,7 @@ import _linkifyString from 'linkifyjs/string';
 import classNames from 'classnames';
 import EMOJIBASE_REGEX from 'emojibase-regex';
 import url from 'url';
+import katex from 'katex';
 
 import {MatrixClientPeg} from './MatrixClientPeg';
 import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
diff --git a/yarn.lock b/yarn.lock
index efc1f0eae1..34b99708fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5607,6 +5607,13 @@ jsx-ast-utils@^2.4.1:
     array-includes "^3.1.1"
     object.assign "^4.1.0"
 
+katex@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9"
+  integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==
+  dependencies:
+    commander "^2.19.0"
+
 kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"

From becc79d67a29a0886f4a6f800daabebae16d655c Mon Sep 17 00:00:00 2001
From: Aleks Kissinger 
Date: Sun, 20 Sep 2020 12:59:22 +0100
Subject: [PATCH 0036/4306] send tex math as data-mx-maths attribute

---
 src/HtmlUtils.tsx       | 26 +++++++++++++++++++++++++-
 src/editor/serialize.ts | 23 ++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx
index 99acbfcb0c..344fb3514c 100644
--- a/src/HtmlUtils.tsx
+++ b/src/HtmlUtils.tsx
@@ -27,6 +27,7 @@ import classNames from 'classnames';
 import EMOJIBASE_REGEX from 'emojibase-regex';
 import url from 'url';
 import katex from 'katex';
+import { AllHtmlEntities } from 'html-entities';
 
 import {MatrixClientPeg} from './MatrixClientPeg';
 import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
@@ -236,7 +237,8 @@ const sanitizeHtmlParams: sanitizeHtml.IOptions = {
     allowedAttributes: {
         // custom ones first:
         font: ['color', 'data-mx-bg-color', 'data-mx-color', 'style'], // custom to matrix
-        span: ['data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'style'], // custom to matrix
+        span: ['data-mx-maths', 'data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'style'], // custom to matrix
+        div: ['data-mx-maths'],
         a: ['href', 'name', 'target', 'rel'], // remote target: custom to matrix
         img: ['src', 'width', 'height', 'alt', 'title'],
         ol: ['start'],
@@ -409,6 +411,27 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
         if (isHtmlMessage) {
             isDisplayedWithHtml = true;
             safeBody = sanitizeHtml(formattedBody, sanitizeParams);
+            if (true) { // TODO: add katex setting
+                const mathDelimiters = [
+                    { left: "
.*?
", display: true }, + { left: ".*?", display: false } + ]; + + mathDelimiters.forEach(function (d) { + var reg = RegExp(d.left + "(.*?)" + d.right, "g"); + + safeBody = safeBody.replace(reg, function(match, p1) { + return katex.renderToString( + AllHtmlEntities.decode(p1), + { + throwOnError: false, + displayMode: d.display, + output: "mathml" + }) + }); + }); + } + } } finally { delete sanitizeParams.textFilter; @@ -450,6 +473,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts 'markdown-body': isHtmlMessage && !emojiBody, }); + return isDisplayedWithHtml ? { @@ -38,7 +39,27 @@ export function mdSerialize(model: EditorModel) { } export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - const md = mdSerialize(model); + var md = mdSerialize(model); + + if (true) { // TODO: add katex setting + const mathDelimiters = [ // TODO: make customizable + { left: "\\$\\$\\$", right: "\\$\\$\\$", display: true }, + { left: "\\$\\$", right: "\\$\\$", display: false } + ]; + + mathDelimiters.forEach(function (d) { + var reg = RegExp(d.left + "(.*?)" + d.right, "g"); + md = md.replace(reg, function(match, p1) { + const p1e = AllHtmlEntities.encode(p1); + if (d.display == true) { + return `
${p1e}
`; + } else { + return `${p1e}`; + } + }); + }); + } + const parser = new Markdown(md); if (!parser.isPlainText() || forceHTML) { return parser.toHTML(); From e78734bbf6b2fbf1ebee530921998ff97c56f203 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 20 Sep 2020 14:20:35 +0100 Subject: [PATCH 0037/4306] Deserialize back to math delimiters for editing --- src/HtmlUtils.tsx | 4 +++- src/editor/deserialize.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 344fb3514c..46bc7b441c 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -534,7 +534,6 @@ export function checkBlockNode(node: Node) { case "H6": case "PRE": case "BLOCKQUOTE": - case "DIV": case "P": case "UL": case "OL": @@ -547,6 +546,9 @@ export function checkBlockNode(node: Node) { case "TH": case "TD": return true; + case "DIV": + // don't treat math nodes as block nodes for deserializing + return !(node as HTMLElement).hasAttribute("data-mx-maths"); default: return false; } diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index ec697b193c..edaa330e50 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -130,6 +130,18 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl } break; } + case "DIV": + case "SPAN": { + // math nodes are translated back into delimited latex strings + if (n.hasAttribute("data-mx-maths")) { + const delim = (n.nodeName == "SPAN") ? "$$" : "$$$"; + const tex = n.getAttribute("data-mx-maths"); + return partCreator.plain(delim + tex + delim); + } else if (!checkDescendInto(n)) { + return partCreator.plain(n.textContent); + } + break; + } case "OL": state.listIndex.push((n).start || 1); /* falls through */ From 428a6b94ff5c34533b8684e5ae8b019a4dbec07c Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 20 Sep 2020 15:07:12 +0100 Subject: [PATCH 0038/4306] math off by default, enable with latex_maths flag --- src/HtmlUtils.tsx | 4 +++- src/editor/serialize.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 46bc7b441c..047a891847 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -28,6 +28,7 @@ import EMOJIBASE_REGEX from 'emojibase-regex'; import url from 'url'; import katex from 'katex'; import { AllHtmlEntities } from 'html-entities'; +import SdkConfig from './SdkConfig'; import {MatrixClientPeg} from './MatrixClientPeg'; import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; @@ -50,6 +51,7 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); // Regex pattern for whitespace characters const WHITESPACE_REGEX = new RegExp("\\s", "g"); + const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})+$`, 'i'); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; @@ -411,7 +413,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts if (isHtmlMessage) { isDisplayedWithHtml = true; safeBody = sanitizeHtml(formattedBody, sanitizeParams); - if (true) { // TODO: add katex setting + if (SdkConfig.get()['latex_maths']) { const mathDelimiters = [ { left: "
.*?
", display: true }, { left: ".*?", display: false } diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 8ec590cba5..72a551a4a3 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -19,6 +19,7 @@ import Markdown from '../Markdown'; import {makeGenericPermalink} from "../utils/permalinks/Permalinks"; import EditorModel from "./model"; import { AllHtmlEntities } from 'html-entities'; +import SdkConfig from '../SdkConfig'; export function mdSerialize(model: EditorModel) { return model.parts.reduce((html, part) => { @@ -41,7 +42,7 @@ export function mdSerialize(model: EditorModel) { export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { var md = mdSerialize(model); - if (true) { // TODO: add katex setting + if (SdkConfig.get()['latex_maths']) { const mathDelimiters = [ // TODO: make customizable { left: "\\$\\$\\$", right: "\\$\\$\\$", display: true }, { left: "\\$\\$", right: "\\$\\$", display: false } From e4448ae1ad87cbd3e47c73a589012494ec7d4189 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 20 Sep 2020 16:52:29 +0100 Subject: [PATCH 0039/4306] send fallback in pre tags, not code --- src/editor/serialize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 72a551a4a3..c0d9509ffa 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -53,9 +53,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = md = md.replace(reg, function(match, p1) { const p1e = AllHtmlEntities.encode(p1); if (d.display == true) { - return `
${p1e}
`; + return `
${p1e}
`; } else { - return `${p1e}`; + return `
${p1e}
`; } }); }); From 7e6d7053e0a6c55f082153a521de079c7db2d77c Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 20 Sep 2020 17:02:27 +0100 Subject: [PATCH 0040/4306] Revert "send fallback in pre tags, not code" (code looks better) This reverts commit e4448ae1ad87cbd3e47c73a589012494ec7d4189. --- src/editor/serialize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index c0d9509ffa..72a551a4a3 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -53,9 +53,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = md = md.replace(reg, function(match, p1) { const p1e = AllHtmlEntities.encode(p1); if (d.display == true) { - return `
${p1e}
`; + return `
${p1e}
`; } else { - return `
${p1e}
`; + return `${p1e}`; } }); }); From 1f24b5b90c9fe6a743db17d14b726e1aefd15f6f Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 20 Sep 2020 17:48:42 +0100 Subject: [PATCH 0041/4306] made math display slightly larger --- res/css/structures/_RoomView.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 572c7166d2..571c34fcb0 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -205,6 +205,10 @@ limitations under the License. clear: both; } +.mx_RoomView_MessageList .katex { + font-size: 1.3em; +} + li.mx_RoomView_myReadMarker_container { height: 0px; margin: 0px; From 24a1834f9b37993b79ec92c1c3081d6aa7777d37 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 21 Sep 2020 09:00:24 +0100 Subject: [PATCH 0042/4306] support multi-line and escaped $ --- src/HtmlUtils.tsx | 6 +++--- src/editor/serialize.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 047a891847..569b1662fe 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -415,12 +415,12 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts safeBody = sanitizeHtml(formattedBody, sanitizeParams); if (SdkConfig.get()['latex_maths']) { const mathDelimiters = [ - { left: "
.*?
", display: true }, - { left: ".*?", display: false } + { pattern: "
(.|\\s)*?
", display: true }, + { pattern: "(.|\\s)*?", display: false } ]; mathDelimiters.forEach(function (d) { - var reg = RegExp(d.left + "(.*?)" + d.right, "g"); + var reg = RegExp(d.pattern, "gm"); safeBody = safeBody.replace(reg, function(match, p1) { return katex.renderToString( diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 72a551a4a3..d0a28266eb 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -44,12 +44,12 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = if (SdkConfig.get()['latex_maths']) { const mathDelimiters = [ // TODO: make customizable - { left: "\\$\\$\\$", right: "\\$\\$\\$", display: true }, - { left: "\\$\\$", right: "\\$\\$", display: false } + { pattern: "\\$\\$\\$(([^$]|\\\\\\$)*)\\$\\$\\$", display: true }, + { pattern: "\\$\\$(([^$]|\\\\\\$)*)\\$\\$", display: false } ]; mathDelimiters.forEach(function (d) { - var reg = RegExp(d.left + "(.*?)" + d.right, "g"); + var reg = RegExp(d.pattern, "gm"); md = md.replace(reg, function(match, p1) { const p1e = AllHtmlEntities.encode(p1); if (d.display == true) { From 4df8754aad0333c840eceb1892faa9f3c90f2405 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 21 Sep 2020 11:00:39 +0100 Subject: [PATCH 0043/4306] allow custom latex delimiters in config.json --- src/editor/deserialize.ts | 10 ++++++++-- src/editor/serialize.ts | 26 ++++++++++++-------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index edaa330e50..e27eecd2db 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -21,6 +21,7 @@ import { walkDOMDepthFirst } from "./dom"; import { checkBlockNode } from "../HtmlUtils"; import { getPrimaryPermalinkEntity } from "../utils/permalinks/Permalinks"; import { PartCreator } from "./parts"; +import SdkConfig from "../SdkConfig"; function parseAtRoomMentions(text: string, partCreator: PartCreator) { const ATROOM = "@room"; @@ -134,9 +135,14 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl case "SPAN": { // math nodes are translated back into delimited latex strings if (n.hasAttribute("data-mx-maths")) { - const delim = (n.nodeName == "SPAN") ? "$$" : "$$$"; + const delimLeft = (n.nodeName == "SPAN") ? + (SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "$$" : + (SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "$$$"; + const delimRight = (n.nodeName == "SPAN") ? + (SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "$$" : + (SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "$$$"; const tex = n.getAttribute("data-mx-maths"); - return partCreator.plain(delim + tex + delim); + return partCreator.plain(delimLeft + tex + delimRight); } else if (!checkDescendInto(n)) { return partCreator.plain(n.textContent); } diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index d0a28266eb..da8ae4e820 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -43,21 +43,19 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = var md = mdSerialize(model); if (SdkConfig.get()['latex_maths']) { - const mathDelimiters = [ // TODO: make customizable - { pattern: "\\$\\$\\$(([^$]|\\\\\\$)*)\\$\\$\\$", display: true }, - { pattern: "\\$\\$(([^$]|\\\\\\$)*)\\$\\$", display: false } - ]; + const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || + "\\$\\$\\$(([^$]|\\\\\\$)*)\\$\\$\\$"; + const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || + "\\$\\$(([^$]|\\\\\\$)*)\\$\\$"; - mathDelimiters.forEach(function (d) { - var reg = RegExp(d.pattern, "gm"); - md = md.replace(reg, function(match, p1) { - const p1e = AllHtmlEntities.encode(p1); - if (d.display == true) { - return `
${p1e}
`; - } else { - return `${p1e}`; - } - }); + md = md.replace(RegExp(displayPattern, "gm"), function(m,p1) { + const p1e = AllHtmlEntities.encode(p1); + return `
${p1e}
`; + }); + + md = md.replace(RegExp(inlinePattern, "gm"), function(m,p1) { + const p1e = AllHtmlEntities.encode(p1); + return `${p1e}`; }); } From 1b689bb4e11c1329072a85002ea90abfaf9043df Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 21 Sep 2020 22:02:19 +0100 Subject: [PATCH 0044/4306] tell markdown to ignore math tags --- src/Markdown.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Markdown.js b/src/Markdown.js index 492450e87d..dc15e7d6b3 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -16,13 +16,19 @@ limitations under the License. import commonmark from 'commonmark'; import {escape} from "lodash"; +import SdkConfig from './SdkConfig'; -const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; +const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u', 'code']; // These types of node are definitely text const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document']; function is_allowed_html_tag(node) { + if (SdkConfig.get()['latex_maths'] && + node.literal.match(/^<\/?(div|span)( data-mx-maths="[^"]*")?>$/) != null) { + return true; + } + // Regex won't work for tags with attrs, but we only // allow anyway. const matches = /^<\/?(.*)>$/.exec(node.literal); @@ -30,6 +36,7 @@ function is_allowed_html_tag(node) { const tag = matches[1]; return ALLOWED_HTML_TAGS.indexOf(tag) > -1; } + return false; } From aded3c9de2b14010612b7d9581b10366d9dc3be2 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Tue, 22 Sep 2020 11:54:23 +0100 Subject: [PATCH 0045/4306] cosmetic changes (lint) --- src/HtmlUtils.tsx | 13 +++++-------- src/editor/serialize.ts | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 569b1662fe..7bccd47622 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -416,24 +416,21 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts if (SdkConfig.get()['latex_maths']) { const mathDelimiters = [ { pattern: "
(.|\\s)*?
", display: true }, - { pattern: "(.|\\s)*?", display: false } + { pattern: "(.|\\s)*?", display: false }, ]; - mathDelimiters.forEach(function (d) { - var reg = RegExp(d.pattern, "gm"); - - safeBody = safeBody.replace(reg, function(match, p1) { + mathDelimiters.forEach(function(d) { + safeBody = safeBody.replace(RegExp(d.pattern, "gm"), function(m, p1) { return katex.renderToString( AllHtmlEntities.decode(p1), { throwOnError: false, displayMode: d.display, - output: "mathml" + output: "mathml", }) }); }); - } - + } } } finally { delete sanitizeParams.textFilter; diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index da8ae4e820..02194a1d59 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -40,7 +40,7 @@ export function mdSerialize(model: EditorModel) { } export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - var md = mdSerialize(model); + let md = mdSerialize(model); if (SdkConfig.get()['latex_maths']) { const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || @@ -48,12 +48,12 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || "\\$\\$(([^$]|\\\\\\$)*)\\$\\$"; - md = md.replace(RegExp(displayPattern, "gm"), function(m,p1) { + md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); return `
${p1e}
`; }); - md = md.replace(RegExp(inlinePattern, "gm"), function(m,p1) { + md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); return `${p1e}`; }); From 0dc5200b0e858f9c922d67fe5323ab241c51513e Mon Sep 17 00:00:00 2001 From: Glandos Date: Thu, 3 Sep 2020 15:10:33 +0200 Subject: [PATCH 0046/4306] Push name to the end, near text, in IRC layout Currently, the name (and aux message) are align to the start, leaving a blank space between the end of the name and the message. In a lot of IRC themes, names (and actions) are aligned to the end, next to the message, for a better readability. Signed-off-by: Adrien CLERC --- 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 958d718b11..ece547d02b 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -186,6 +186,7 @@ $irc-line-height: $font-18px; overflow: hidden; text-overflow: ellipsis; min-width: var(--name-width); + text-align: end; } } } From d2054ea685bad49af11ec9a64b5aa4218bc204c0 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Fri, 25 Sep 2020 09:05:22 +0100 Subject: [PATCH 0047/4306] HTML output for cross-browser support --- res/css/structures/_RoomView.scss | 4 ---- src/HtmlUtils.tsx | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 571c34fcb0..572c7166d2 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -205,10 +205,6 @@ limitations under the License. clear: both; } -.mx_RoomView_MessageList .katex { - font-size: 1.3em; -} - li.mx_RoomView_myReadMarker_container { height: 0px; margin: 0px; diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 7bccd47622..70a2a3f000 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -426,7 +426,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts { throwOnError: false, displayMode: d.display, - output: "mathml", + output: "htmlAndMathml", }) }); }); From 43ff97c1789be080b8ec69e3045d7a262e3dfd31 Mon Sep 17 00:00:00 2001 From: Daniel Maslowski Date: Wed, 9 Sep 2020 20:35:26 +0200 Subject: [PATCH 0048/4306] Add support for giving reason when redacting Signed-off-by: Daniel Maslowski --- src/components/views/context_menus/MessageContextMenu.js | 4 +++- src/components/views/dialogs/ConfirmRedactDialog.js | 8 +++++--- src/i18n/strings/en_EN.json | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index d760c8defa..b6d27e45f9 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -145,7 +145,7 @@ export default class MessageContextMenu extends React.Component { onRedactClick = () => { const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { - onFinished: async (proceed) => { + onFinished: async (proceed, reason) => { if (!proceed) return; const cli = MatrixClientPeg.get(); @@ -153,6 +153,8 @@ export default class MessageContextMenu extends React.Component { await cli.redactEvent( this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(), + undefined, + reason ? { reason } : {}, ); } catch (e) { const code = e.errcode || e.statusCode; diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js index 3106df1d5b..2216f9a93a 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.js +++ b/src/components/views/dialogs/ConfirmRedactDialog.js @@ -23,15 +23,17 @@ import { _t } from '../../../languageHandler'; */ export default class ConfirmRedactDialog extends React.Component { render() { - const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog'); + const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog'); return ( - - + ); } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 65374ea3ec..ecc4bd2f4c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1624,6 +1624,7 @@ "Removing…": "Removing…", "Confirm Removal": "Confirm Removal", "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.": "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.", + "Reason (optional)": "Reason (optional)", "Clear all data in this session?": "Clear all data in this session?", "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.", "Clear all data": "Clear all data", From 65c4460abcdb64bac14bdd72e3b970a96dd52299 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Fri, 9 Oct 2020 15:47:11 +0100 Subject: [PATCH 0049/4306] whitespace fixes --- src/HtmlUtils.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 70a2a3f000..da3eb3b128 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -51,7 +51,6 @@ const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); // Regex pattern for whitespace characters const WHITESPACE_REGEX = new RegExp("\\s", "g"); - const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})+$`, 'i'); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; @@ -472,7 +471,6 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts 'markdown-body': isHtmlMessage && !emojiBody, }); - return isDisplayedWithHtml ? Date: Sat, 10 Oct 2020 09:12:53 +0100 Subject: [PATCH 0050/4306] only allow code tags inside math tag --- src/Markdown.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Markdown.js b/src/Markdown.js index dc15e7d6b3..9914cff85a 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -18,14 +18,21 @@ import commonmark from 'commonmark'; import {escape} from "lodash"; import SdkConfig from './SdkConfig'; -const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u', 'code']; +const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; // These types of node are definitely text const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document']; +function is_math_node(node) { + return node != null && + node.literal != null && + node.literal.match(/^<((div|span) data-mx-maths="[^"]*"|\/(div|span))>$/) != null; +} + function is_allowed_html_tag(node) { if (SdkConfig.get()['latex_maths'] && - node.literal.match(/^<\/?(div|span)( data-mx-maths="[^"]*")?>$/) != null) { + (is_math_node(node) || + (node.literal.match(/^<\/?code>$/) && is_math_node(node.parent)))) { return true; } From 96742fc3093cc88cd609d731d932a05ab094262f Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 10 Oct 2020 16:32:49 +0100 Subject: [PATCH 0051/4306] latex math as labs setting --- src/HtmlUtils.tsx | 4 ++-- src/Markdown.js | 4 ++-- src/editor/serialize.ts | 3 ++- src/settings/Settings.ts | 6 ++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index da3eb3b128..ca718cd9aa 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -28,7 +28,7 @@ import EMOJIBASE_REGEX from 'emojibase-regex'; import url from 'url'; import katex from 'katex'; import { AllHtmlEntities } from 'html-entities'; -import SdkConfig from './SdkConfig'; +import SettingsStore from './settings/SettingsStore'; import {MatrixClientPeg} from './MatrixClientPeg'; import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; @@ -412,7 +412,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts if (isHtmlMessage) { isDisplayedWithHtml = true; safeBody = sanitizeHtml(formattedBody, sanitizeParams); - if (SdkConfig.get()['latex_maths']) { + if (SettingsStore.getValue("feature_latex_maths")) { const mathDelimiters = [ { pattern: "
(.|\\s)*?
", display: true }, { pattern: "(.|\\s)*?", display: false }, diff --git a/src/Markdown.js b/src/Markdown.js index 9914cff85a..329dcdd996 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -16,7 +16,7 @@ limitations under the License. import commonmark from 'commonmark'; import {escape} from "lodash"; -import SdkConfig from './SdkConfig'; +import SettingsStore from './settings/SettingsStore'; const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; @@ -30,7 +30,7 @@ function is_math_node(node) { } function is_allowed_html_tag(node) { - if (SdkConfig.get()['latex_maths'] && + if (SettingsStore.getValue("feature_latex_maths") && (is_math_node(node) || (node.literal.match(/^<\/?code>$/) && is_math_node(node.parent)))) { return true; diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 02194a1d59..9f24cd5eb2 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -19,6 +19,7 @@ import Markdown from '../Markdown'; import {makeGenericPermalink} from "../utils/permalinks/Permalinks"; import EditorModel from "./model"; import { AllHtmlEntities } from 'html-entities'; +import SettingsStore from '../settings/SettingsStore'; import SdkConfig from '../SdkConfig'; export function mdSerialize(model: EditorModel) { @@ -42,7 +43,7 @@ export function mdSerialize(model: EditorModel) { export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { let md = mdSerialize(model); - if (SdkConfig.get()['latex_maths']) { + if (SettingsStore.getValue("feature_latex_maths")) { const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || "\\$\\$\\$(([^$]|\\\\\\$)*)\\$\\$\\$"; const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 737c882919..2f817c264c 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -116,6 +116,12 @@ export interface ISetting { } export const SETTINGS: {[setting: string]: ISetting} = { + "feature_latex_maths": { + isFeature: true, + displayName: _td("LaTeX math in messages"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_communities_v2_prototypes": { isFeature: true, displayName: _td( From a89adb86a5912d3ce71171583181175fe2564a23 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 10 Oct 2020 16:33:25 +0100 Subject: [PATCH 0052/4306] i18n en+nl for latex math labs setting --- src/i18n/strings/en_EN.json | 1 + src/i18n/strings/en_US.json | 1 + src/i18n/strings/nl.json | 1 + 3 files changed, 3 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d7360430ae..d7b40fc198 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -856,6 +856,7 @@ "click to reveal": "click to reveal", "Clear cache and reload": "Clear cache and reload", "Labs": "Labs", + "LaTeX math in messages": "LaTeX math in messages", "Customise your experience with experimental labs features. Learn more.": "Customise your experience with experimental labs features. Learn more.", "Ignored/Blocked": "Ignored/Blocked", "Error adding ignored user/server": "Error adding ignored user/server", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index a1275fb089..c00bf03b29 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -128,6 +128,7 @@ "Kick": "Kick", "Kicks user with given id": "Kicks user with given id", "Labs": "Labs", + "LaTeX math in messages": "LaTeX math in messages", "Ignore": "Ignore", "Unignore": "Unignore", "You are now ignoring %(userId)s": "You are now ignoring %(userId)s", diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index bb0fb5def6..d991962eec 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -199,6 +199,7 @@ "%(targetName)s joined the room.": "%(targetName)s is tot het gesprek toegetreden.", "Jump to first unread message.": "Spring naar het eerste ongelezen bericht.", "Labs": "Experimenteel", + "LaTeX math in messages": "LaTeX wiskunde in berichten", "Last seen": "Laatst gezien", "Leave room": "Gesprek verlaten", "%(targetName)s left the room.": "%(targetName)s heeft het gesprek verlaten.", From bdd332c8b5366398d4af166b49b3eaf1cddb6230 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sat, 10 Oct 2020 20:05:35 +0100 Subject: [PATCH 0053/4306] ran yarn i18n --- 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 a33104ab12..b41a19aa21 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -438,6 +438,7 @@ "%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s", "%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s", "Change notification settings": "Change notification settings", + "LaTeX math in messages": "LaTeX math in messages", "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.", "New spinner design": "New spinner design", "Message Pinning": "Message Pinning", @@ -848,7 +849,6 @@ "click to reveal": "click to reveal", "Clear cache and reload": "Clear cache and reload", "Labs": "Labs", - "LaTeX math in messages": "LaTeX math in messages", "Customise your experience with experimental labs features. Learn more.": "Customise your experience with experimental labs features. Learn more.", "Ignored/Blocked": "Ignored/Blocked", "Error adding ignored user/server": "Error adding ignored user/server", From f0c4473107d0c3589479809d8accd79b9c4dba08 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 12 Oct 2020 21:01:11 +0100 Subject: [PATCH 0054/4306] tell markdown parser to ignore properly-formatted math tags --- src/Markdown.js | 51 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Markdown.js b/src/Markdown.js index 329dcdd996..564a2ed0a8 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -23,19 +23,47 @@ const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; // These types of node are definitely text const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document']; -function is_math_node(node) { - return node != null && - node.literal != null && - node.literal.match(/^<((div|span) data-mx-maths="[^"]*"|\/(div|span))>$/) != null; +// prevent renderer from interpreting contents of AST node +function freeze_node(walker, node) { + const newNode = new commonmark.Node('custom_inline', node.sourcepos); + newNode.onEnter = node.literal; + node.insertAfter(newNode); + node.unlink(); + walker.resumeAt(newNode.next, true); +} + +// prevent renderer from interpreting contents of latex math tags +function freeze_math(parsed) { + const walker = parsed.walker(); + let ev; + let inMath = false; + while ( (ev = walker.next()) ) { + const node = ev.node; + if (ev.entering) { + if (!inMath) { + // entering a math tag + if (node.literal != null && node.literal.match('^<(div|span) data-mx-maths="[^"]*">$') != null) { + inMath = true; + freeze_node(walker, node); + } + } else { + // math tags should only contain a single code block, with URL-escaped latex as fallback output + if (node.literal != null && node.literal.match('^(||[^<>]*)$')) { + freeze_node(walker, node); + // leave when span or div is closed + } else if (node.literal == '
' || node.literal == '') { + inMath = false; + freeze_node(walker, node); + // this case only happens if we have improperly formatted math tags, so bail + } else { + inMath = false; + } + } + } + } } function is_allowed_html_tag(node) { - if (SettingsStore.getValue("feature_latex_maths") && - (is_math_node(node) || - (node.literal.match(/^<\/?code>$/) && is_math_node(node.parent)))) { - return true; - } - // Regex won't work for tags with attrs, but we only // allow anyway. const matches = /^<\/?(.*)>$/.exec(node.literal); @@ -173,6 +201,9 @@ export default class Markdown { */ }; + // prevent strange behaviour when mixing latex math and markdown + freeze_math(this.parsed); + return renderer.render(this.parsed); } From 38d1aac978d49160bed9c96b2a1205a4e7fb707f Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 12 Oct 2020 21:15:38 +0100 Subject: [PATCH 0055/4306] removed useless import and whitespace --- src/Markdown.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Markdown.js b/src/Markdown.js index 564a2ed0a8..2e6f391818 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -16,7 +16,6 @@ limitations under the License. import commonmark from 'commonmark'; import {escape} from "lodash"; -import SettingsStore from './settings/SettingsStore'; const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; @@ -71,7 +70,6 @@ function is_allowed_html_tag(node) { const tag = matches[1]; return ALLOWED_HTML_TAGS.indexOf(tag) > -1; } - return false; } From f36651f5380f1c119577495622365a015b34cdba Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Sat, 26 Sep 2020 23:21:16 +0200 Subject: [PATCH 0056/4306] Add keyboard shortcut to close current conversations Signed-off-by: Heiko Carrasco --- src/accessibility/KeyboardShortcuts.tsx | 6 ++++++ src/components/structures/LoggedInView.tsx | 12 +++++++++++- src/i18n/strings/en_EN.json | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index 58d8124122..48d0eb2ab1 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -257,6 +257,12 @@ const shortcuts: Record = { key: Key.SLASH, }], description: _td("Toggle this dialog"), + }, { + keybinds: [{ + modifiers: [CMD_OR_CTRL, Modifiers.ALT], + key: Key.H, + }], + description: _td("Go to Home View"), }, ], diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 79f2916200..e7256e4cd4 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -21,7 +21,7 @@ import * as PropTypes from 'prop-types'; import { MatrixClient } from 'matrix-js-sdk/src/client'; import { DragDropContext } from 'react-beautiful-dnd'; -import {Key, isOnlyCtrlOrCmdKeyEvent, isOnlyCtrlOrCmdIgnoreShiftKeyEvent} from '../../Keyboard'; +import {Key, isOnlyCtrlOrCmdKeyEvent, isOnlyCtrlOrCmdIgnoreShiftKeyEvent, isMac} from '../../Keyboard'; import PageTypes from '../../PageTypes'; import CallMediaHandler from '../../CallMediaHandler'; import { fixupColorFonts } from '../../utils/FontManager'; @@ -400,6 +400,7 @@ class LoggedInView extends React.Component { const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev); const hasModifier = ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey; const isModifier = ev.key === Key.ALT || ev.key === Key.CONTROL || ev.key === Key.META || ev.key === Key.SHIFT; + const modKey = isMac ? ev.metaKey : ev.ctrlKey; switch (ev.key) { case Key.PAGE_UP: @@ -444,6 +445,15 @@ class LoggedInView extends React.Component { } break; + case Key.H: + if (ev.altKey && modKey) { + dis.dispatch({ + action: 'view_home_page', + }); + handled = true; + } + break; + case Key.ARROW_UP: case Key.ARROW_DOWN: if (ev.altKey && !ev.ctrlKey && !ev.metaKey) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index eb8f9100ec..0bc430d87a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2350,6 +2350,7 @@ "Activate selected button": "Activate selected button", "Toggle right panel": "Toggle right panel", "Toggle this dialog": "Toggle this dialog", + "Go to Home View": "Go to Home View", "Move autocomplete selection up/down": "Move autocomplete selection up/down", "Cancel autocomplete": "Cancel autocomplete", "Page Up": "Page Up", From d7f15985f590232a72be980570d98d81b7efd45c Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Tue, 13 Oct 2020 18:26:48 +0200 Subject: [PATCH 0057/4306] Close all active modals when home shortcut is used Signed-off-by: Heiko Carrasco --- src/Modal.tsx | 9 +++++++++ src/components/structures/LoggedInView.tsx | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/Modal.tsx b/src/Modal.tsx index b0f6ef988e..b6074e4807 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -147,6 +147,15 @@ export class ModalManager { return this.appendDialogAsync(...rest); } + public closeCurrentModal(reason: string) { + const modal = this.getCurrentModal(); + if (!modal) { + return; + } + modal.closeReason = reason; + modal.close(); + } + private buildModal( prom: Promise, props?: IProps, diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index e7256e4cd4..98921d03a1 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -52,6 +52,7 @@ import RoomListStore from "../../stores/room-list/RoomListStore"; import NonUrgentToastContainer from "./NonUrgentToastContainer"; import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; +import Modal from "../../Modal"; // 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. @@ -450,6 +451,7 @@ class LoggedInView extends React.Component { dis.dispatch({ action: 'view_home_page', }); + Modal.closeCurrentModal("homeKeyboardShortcut"); handled = true; } break; From cc713aff72c56478edb4f1eafbdc55b8c9fd4248 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Wed, 14 Oct 2020 09:35:57 +0100 Subject: [PATCH 0058/4306] add fallback output in code block AFTER markdown processing --- src/Markdown.js | 49 +++++------------------------------------ src/editor/serialize.ts | 18 ++++++++++++--- 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/Markdown.js b/src/Markdown.js index 2e6f391818..dc4d442aff 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -22,47 +22,12 @@ const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; // These types of node are definitely text const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document']; -// prevent renderer from interpreting contents of AST node -function freeze_node(walker, node) { - const newNode = new commonmark.Node('custom_inline', node.sourcepos); - newNode.onEnter = node.literal; - node.insertAfter(newNode); - node.unlink(); - walker.resumeAt(newNode.next, true); -} - -// prevent renderer from interpreting contents of latex math tags -function freeze_math(parsed) { - const walker = parsed.walker(); - let ev; - let inMath = false; - while ( (ev = walker.next()) ) { - const node = ev.node; - if (ev.entering) { - if (!inMath) { - // entering a math tag - if (node.literal != null && node.literal.match('^<(div|span) data-mx-maths="[^"]*">$') != null) { - inMath = true; - freeze_node(walker, node); - } - } else { - // math tags should only contain a single code block, with URL-escaped latex as fallback output - if (node.literal != null && node.literal.match('^(||[^<>]*)$')) { - freeze_node(walker, node); - // leave when span or div is closed - } else if (node.literal == '
' || node.literal == '') { - inMath = false; - freeze_node(walker, node); - // this case only happens if we have improperly formatted math tags, so bail - } else { - inMath = false; - } - } - } - } -} - function is_allowed_html_tag(node) { + if (node.literal != null && + node.literal.match('^<((div|span) data-mx-maths="[^"]*"|\/(div|span))>$') != null) { + return true; + } + // Regex won't work for tags with attrs, but we only // allow anyway. const matches = /^<\/?(.*)>$/.exec(node.literal); @@ -70,6 +35,7 @@ function is_allowed_html_tag(node) { const tag = matches[1]; return ALLOWED_HTML_TAGS.indexOf(tag) > -1; } + return false; } @@ -199,9 +165,6 @@ export default class Markdown { */ }; - // prevent strange behaviour when mixing latex math and markdown - freeze_math(this.parsed); - return renderer.render(this.parsed); } diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 9f24cd5eb2..88fd1c90fc 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -21,6 +21,7 @@ import EditorModel from "./model"; import { AllHtmlEntities } from 'html-entities'; import SettingsStore from '../settings/SettingsStore'; import SdkConfig from '../SdkConfig'; +import cheerio from 'cheerio'; export function mdSerialize(model: EditorModel) { return model.parts.reduce((html, part) => { @@ -51,18 +52,29 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); - return `
${p1e}
`; + return `
`; }); md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); - return `${p1e}`; + return ``; }); } const parser = new Markdown(md); if (!parser.isPlainText() || forceHTML) { - return parser.toHTML(); + // feed Markdown output to HTML parser + const phtml = cheerio.load(parser.toHTML(), + { _useHtmlParser2: true, decodeEntities: false }) + + // add fallback output for latex math, which should not be interpreted as markdown + phtml('div, span').each(function() { + const tex = phtml(this).attr('data-mx-maths') + if (tex) { + phtml(this).html(`${tex}`) + } + }); + return phtml.html(); } // ensure removal of escape backslashes in non-Markdown messages if (md.indexOf("\\") > -1) { From 10b732131a7315aca652677857a285d7dabb243b Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Wed, 14 Oct 2020 22:16:28 +0100 Subject: [PATCH 0059/4306] use html parser rather than regexes --- src/HtmlUtils.tsx | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 6bae0b25b6..dc2f45210b 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -30,6 +30,7 @@ import url from 'url'; import katex from 'katex'; import { AllHtmlEntities } from 'html-entities'; import SettingsStore from './settings/SettingsStore'; +import cheerio from 'cheerio'; import {MatrixClientPeg} from './MatrixClientPeg'; import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; @@ -414,23 +415,20 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts if (isHtmlMessage) { isDisplayedWithHtml = true; safeBody = sanitizeHtml(formattedBody, sanitizeParams); - if (SettingsStore.getValue("feature_latex_maths")) { - const mathDelimiters = [ - { pattern: "
(.|\\s)*?
", display: true }, - { pattern: "(.|\\s)*?", display: false }, - ]; + const phtml = cheerio.load(safeBody, + { _useHtmlParser2: true, decodeEntities: false }) - mathDelimiters.forEach(function(d) { - safeBody = safeBody.replace(RegExp(d.pattern, "gm"), function(m, p1) { - return katex.renderToString( - AllHtmlEntities.decode(p1), - { - throwOnError: false, - displayMode: d.display, - output: "htmlAndMathml", - }) - }); + if (SettingsStore.getValue("feature_latex_maths")) { + phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) { + return katex.renderToString( + AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')), + { + throwOnError: false, + displayMode: e.name == 'div', + output: "htmlAndMathml", + }); }); + safeBody = phtml.html(); } } } finally { From 7506e9a85de9a7a1cb4b5836dc3ca4b234dd17b2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Oct 2020 15:58:18 +0100 Subject: [PATCH 0060/4306] Disable notifications for the room you have recently been active in Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Notifier.ts b/src/Notifier.ts index 1899896f9b..6460be20ad 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -34,6 +34,8 @@ import SettingsStore from "./settings/SettingsStore"; import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast"; import {SettingLevel} from "./settings/SettingLevel"; import {isPushNotifyDisabled} from "./settings/controllers/NotificationControllers"; +import RoomViewStore from "./stores/RoomViewStore"; +import UserActivity from "./UserActivity"; /* * Dispatches: @@ -376,6 +378,11 @@ export const Notifier = { const room = MatrixClientPeg.get().getRoom(ev.getRoomId()); const actions = MatrixClientPeg.get().getPushActionsForEvent(ev); if (actions && actions.notify) { + if (RoomViewStore.getRoomId() === room.roomId && UserActivity.sharedInstance().userActiveRecently()) { + // don't bother notifying as user was recently active in this room + return; + } + if (this.isEnabled()) { this._displayPopupNotification(ev, room); } From 176c7c32da6c8e8314889f6b8bbaa632b03c4f2d Mon Sep 17 00:00:00 2001 From: Bryan Kok Date: Sat, 17 Oct 2020 14:35:11 +0800 Subject: [PATCH 0061/4306] Search through the list of unfiltered rooms rather than the rooms in the state which are already filtered by the search text --- src/components/views/rooms/RoomSublist.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 4056f2fbd4..111692786c 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -420,7 +420,7 @@ export default class RoomSublist extends React.Component { room = this.state.rooms && this.state.rooms[0]; } else { // find the first room with a count of the same colour as the badge count - room = this.state.rooms.find((r: Room) => { + room = RoomListStore.instance.unfilteredLists[this.props.tagId].find((r: Room) => { const notifState = this.notificationState.getForRoom(r); return notifState.count > 0 && notifState.color === this.notificationState.color; }); From 7d22bbc00f49356cd5fec3565a19376ef8b0ef05 Mon Sep 17 00:00:00 2001 From: Bryan Kok Date: Sat, 17 Oct 2020 23:52:18 +0800 Subject: [PATCH 0062/4306] Trim spurious whitespace of nicknames --- src/components/views/settings/ProfileSettings.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/ProfileSettings.js b/src/components/views/settings/ProfileSettings.js index 92851ccaa0..294f80acd1 100644 --- a/src/components/views/settings/ProfileSettings.js +++ b/src/components/views/settings/ProfileSettings.js @@ -77,10 +77,12 @@ export default class ProfileSettings extends React.Component { const client = MatrixClientPeg.get(); const newState = {}; + const displayName = this.state.displayName.trim(); try { if (this.state.originalDisplayName !== this.state.displayName) { - await client.setDisplayName(this.state.displayName); - newState.originalDisplayName = this.state.displayName; + await client.setDisplayName(displayName); + newState.originalDisplayName = displayName; + newState.displayName = displayName; } if (this.state.avatarFile) { From fcbaea640daf3a036d55cb1bda5d7fed552c2d4e Mon Sep 17 00:00:00 2001 From: Bryan Kok Date: Sun, 18 Oct 2020 14:36:50 +0800 Subject: [PATCH 0063/4306] Trim room names changed through the UI --- src/components/views/room_settings/RoomProfileSettings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js index ca09c3093a..b894663c16 100644 --- a/src/components/views/room_settings/RoomProfileSettings.js +++ b/src/components/views/room_settings/RoomProfileSettings.js @@ -95,10 +95,11 @@ export default class RoomProfileSettings extends React.Component { const newState = {}; // TODO: What do we do about errors? - + const displayName = this.state.displayName.trim(); if (this.state.originalDisplayName !== this.state.displayName) { - await client.setRoomName(this.props.roomId, this.state.displayName); - newState.originalDisplayName = this.state.displayName; + await client.setRoomName(this.props.roomId, displayName); + newState.originalDisplayName = displayName; + newState.displayName = displayName; } if (this.state.avatarFile) { From 091143db525e9eb1a563bd097333b2a6e644af22 Mon Sep 17 00:00:00 2001 From: Resynth Date: Sun, 18 Oct 2020 22:40:19 +0100 Subject: [PATCH 0064/4306] Add border-radius for video Images have a rounded border, so we may as well add it to videos. Works fine in Chrome, and in other spec-compliant browsers. Signed-off-by: Resynth --- res/css/views/messages/_MVideoBody.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/messages/_MVideoBody.scss b/res/css/views/messages/_MVideoBody.scss index 3b05c53f34..ac3491bc8f 100644 --- a/res/css/views/messages/_MVideoBody.scss +++ b/res/css/views/messages/_MVideoBody.scss @@ -18,5 +18,6 @@ span.mx_MVideoBody { video.mx_MVideoBody { max-width: 100%; height: auto; + border-radius: 4px; } } From 6e97baf09f80364d157233a8567c2c1bc106f302 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 12:53:17 +0200 Subject: [PATCH 0065/4306] Added license --- src/components/views/elements/Confetti.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index b0f88dedb7..bef67cddcc 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -1,3 +1,23 @@ +/* +MIT License +Copyright (c) 2018 MathuSum Mut +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + const confetti = { //set max confetti count maxCount: 150, From 3358ed27921d137a2880f00dc0a1c603126b9bca Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 12:57:57 +0200 Subject: [PATCH 0066/4306] Use arrow functions --- src/components/structures/RoomView.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 92f43c75ca..53a964fbb8 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -754,14 +754,14 @@ export default createReactClass({ } } }, - onEventDecrypted(ev) { - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; - this.handleConfetti(ev); - } + onEventDecrypted: (ev) => { + if (!SettingsStore.getValue('dontShowChatEffects')) { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || + this.state.room.getUnreadNotificationCount() === 0) return; + this.handleConfetti(ev); + } }, - handleConfetti(ev) { + handleConfetti: (ev) => { if (this.state.matrixClientIsReady) { const messageBody = _t('sends confetti'); if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { From 4106f70218ef41e6101752e388b54b481cbe0576 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:24:22 +0200 Subject: [PATCH 0067/4306] Fixed merge error --- src/settings/Settings.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 99be728acc..b9ad834c83 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -626,6 +626,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("Don't show chat effects"), default: false, + }, "Widgets.pinned": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: {}, From 929cc48cef28431cc059055240396ec7a2d173bb Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:30:52 +0200 Subject: [PATCH 0068/4306] Fixed eslint error --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 709864bff6..7094a8de1b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -247,7 +247,7 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.on("Event.decrypted", this.onEventDecrypted); + this.context.on("Event.decrypted", this.onEventDecrypted); this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); From 3e8e817a3d51b11c6bffefcff2edbef0dd053e6f Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:35:18 +0200 Subject: [PATCH 0069/4306] Fixed merge error --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 7094a8de1b..f3ec8b8104 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -693,7 +693,7 @@ export default class RoomView extends React.Component { this.checkIfAlone(this.state.room); break; case 'confetti': - animateConfetti(this._roomView.current.offsetWidth); + animateConfetti(this.roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( From 41160ff08e827e63851a3823be442d9395771ca6 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:54:09 +0200 Subject: [PATCH 0070/4306] Render confetti the react way --- src/components/structures/RoomView.tsx | 8 +++- src/components/views/elements/Confetti.js | 30 ++++---------- .../views/elements/ConfettiOverlay.tsx | 41 +++++++++++++++++++ 3 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 src/components/views/elements/ConfettiOverlay.tsx diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index f3ec8b8104..0905005cf7 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,6 +73,7 @@ import TintableSvg from "../views/elements/TintableSvg"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; +import ConfettiOverlay from "../views/elements/ConfettiOverlay"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -693,7 +694,7 @@ export default class RoomView extends React.Component { this.checkIfAlone(this.state.room); break; case 'confetti': - animateConfetti(this.roomView.current.offsetWidth); + //TODO: animateConfetti(this.roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( @@ -853,7 +854,7 @@ export default class RoomView extends React.Component { this.calculateRecommendedVersion(room); this.updateE2EStatus(room); this.updatePermissions(room); - forceStopConfetti(); + //TODO: forceStopConfetti(); }; private async calculateRecommendedVersion(room: Room) { @@ -2072,6 +2073,9 @@ export default class RoomView extends React.Component { return (
+ {this.roomView.current && + + } { + const resize = () => { + const canvas = canvasRef.current; + canvas.height = window.innerHeight; + }; + const canvas = canvasRef.current; + canvas.width = roomWidth; + canvas.height = window.innerHeight; + window.addEventListener("resize", resize, true); + animateConfetti(canvas, roomWidth); + return () => { + window.removeEventListener("resize", resize); + forceStopConfetti(); + }; + }, []); + // on roomWidth change + + useEffect(() => { + const canvas = canvasRef.current; + canvas.width = roomWidth; + }, [roomWidth]); + return ( + + ) +} \ No newline at end of file From 607e33febaa4a6142776fe0126f850d782cca143 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 21:25:01 +0200 Subject: [PATCH 0071/4306] Extensibility, TypeScript and lazy loading --- src/SlashCommands.tsx | 10 +- src/components/structures/RoomView.tsx | 38 +--- src/components/views/elements/Confetti.js | 207 ------------------ .../views/elements/ConfettiOverlay.tsx | 41 ---- .../views/elements/effects/EffectsOverlay.tsx | 77 +++++++ .../views/elements/effects/ICanvasEffect.ts | 5 + .../views/elements/effects/confetti/index.ts | 197 +++++++++++++++++ .../views/rooms/SendMessageComposer.js | 8 +- .../tabs/user/PreferencesUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.ts | 6 +- 11 files changed, 296 insertions(+), 297 deletions(-) delete mode 100644 src/components/views/elements/Confetti.js delete mode 100644 src/components/views/elements/ConfettiOverlay.tsx create mode 100644 src/components/views/elements/effects/EffectsOverlay.tsx create mode 100644 src/components/views/elements/effects/ICanvasEffect.ts create mode 100644 src/components/views/elements/effects/confetti/index.ts diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 87dc1ccdfd..316249d74d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -77,6 +77,7 @@ export const CommandCategories = { "actions": _td("Actions"), "admin": _td("Admin"), "advanced": _td("Advanced"), + "effects": _td("Effects"), "other": _td("Other"), }; @@ -1045,19 +1046,16 @@ export const Commands = [ args: '', runFn: function(roomId, args) { return success((async () => { - const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); - if ((!args) || (!args && isChatEffectsDisabled)) { + if (!args) { args = _t("sends confetti"); MatrixClientPeg.get().sendEmoteMessage(roomId, args); } else { MatrixClientPeg.get().sendTextMessage(roomId, args); } - if (!isChatEffectsDisabled) { - dis.dispatch({action: 'confetti'}); - } + dis.dispatch({action: 'effects.confetti'}); })()); }, - category: CommandCategories.actions, + category: CommandCategories.effects, }), // Command definitions for autocompletion ONLY: diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0905005cf7..1b47386789 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -56,7 +56,6 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import {E2EStatus, shieldStatusForRoom} from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; -import {animateConfetti, forceStopConfetti, isConfettiEmoji} from "../views/elements/Confetti"; import {RightPanelPhases} from "../../stores/RightPanelStorePhases"; import {IMatrixClientCreds} from "../../MatrixClientPeg"; import ScrollPanel from "./ScrollPanel"; @@ -73,7 +72,7 @@ import TintableSvg from "../views/elements/TintableSvg"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; -import ConfettiOverlay from "../views/elements/ConfettiOverlay"; +import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -248,8 +247,6 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.on("Event.decrypted", this.onEventDecrypted); - this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate); @@ -570,8 +567,6 @@ export default class RoomView extends React.Component { this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.removeListener("Event.decrypted", this.onEventDecrypted); - this.context.removeListener("event", this.onEvent); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -693,9 +688,6 @@ export default class RoomView extends React.Component { case 'message_sent': this.checkIfAlone(this.state.room); break; - case 'confetti': - //TODO: animateConfetti(this.roomView.current.offsetWidth); - break; case 'post_sticker_message': this.injectSticker( payload.data.content.url, @@ -804,28 +796,6 @@ export default class RoomView extends React.Component { } }; - private onEventDecrypted = (ev) => { - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; - this.handleConfetti(ev); - } - }; - - private onEvent = (ev) => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); - }; - - private handleConfetti = (ev) => { - if (this.state.matrixClientIsReady) { - const messageBody = _t('sends confetti'); - if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { - dis.dispatch({action: 'confetti'}); - } - } - }; - private onRoomName = (room: Room) => { if (this.state.room && room.roomId == this.state.room.roomId) { this.forceUpdate(); @@ -2070,11 +2040,13 @@ export default class RoomView extends React.Component { mx_RoomView_inCall: Boolean(activeCall), }); + const showChatEffects = SettingsStore.getValue('showChatEffects'); + return (
- {this.roomView.current && - + {showChatEffects && this.roomView.current && + } confetti.frameInterval) { - context.clearRect(0, 0, window.innerWidth, window.innerHeight); - updateParticles(); - drawParticles(context); - lastFrameTime = now - (delta % confetti.frameInterval); - } - requestAnimationFrame(runAnimation); - } - } - - function startConfetti(canvas, roomWidth, timeout) { - window.requestAnimationFrame = (function() { - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, confetti.frameInterval); - }; - })(); - if (context === null) { - context = canvas.getContext("2d"); - } - const count = confetti.maxCount; - while (particles.length < count) { - particles.push(resetParticle({}, canvas.width, canvas.height)); - } - streamingConfetti = true; - runAnimation(); - if (timeout) { - window.setTimeout(stopConfetti, timeout); - } - } - - function stopConfetti() { - streamingConfetti = false; - } - - function removeConfetti() { - stop(); - particles = []; - } - - function isConfettiRunning() { - return streamingConfetti; - } - - function drawParticles(context) { - let particle; - let x; let x2; let y2; - for (let i = 0; i < particles.length; i++) { - particle = particles[i]; - context.beginPath(); - context.lineWidth = particle.diameter; - x2 = particle.x + particle.tilt; - x = x2 + particle.diameter / 2; - y2 = particle.y + particle.tilt + particle.diameter / 2; - if (confetti.gradient) { - const gradient = context.createLinearGradient(x, particle.y, x2, y2); - gradient.addColorStop("0", particle.color); - gradient.addColorStop("1.0", particle.color2); - context.strokeStyle = gradient; - } else { - context.strokeStyle = particle.color; - } - context.moveTo(x, particle.y); - context.lineTo(x2, y2); - context.stroke(); - } - } - - function updateParticles() { - const width = window.innerWidth; - const height = window.innerHeight; - let particle; - waveAngle += 0.01; - for (let i = 0; i < particles.length; i++) { - particle = particles[i]; - if (!streamingConfetti && particle.y < -15) { - particle.y = height + 100; - } else { - particle.tiltAngle += particle.tiltAngleIncrement; - particle.x += Math.sin(waveAngle) - 0.5; - particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5; - particle.tilt = Math.sin(particle.tiltAngle) * 15; - } - if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { - if (streamingConfetti && particles.length <= confetti.maxCount) { - resetParticle(particle, width, height); - } else { - particles.splice(i, 1); - i--; - } - } - } - } -})(); - -export function convertToHex(content) { - const contentBodyToHexArray = []; - let hex; - if (content.body) { - for (let i = 0; i < content.body.length; i++) { - hex = content.body.codePointAt(i).toString(16); - contentBodyToHexArray.push(hex); - } - } - return contentBodyToHexArray; -} - -export function isConfettiEmoji(content) { - const hexArray = convertToHex(content); - return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); -} - -export function animateConfetti(canvas, roomWidth) { - confetti.start(canvas, roomWidth, 3000); -} -export function forceStopConfetti() { - confetti.remove(); -} diff --git a/src/components/views/elements/ConfettiOverlay.tsx b/src/components/views/elements/ConfettiOverlay.tsx deleted file mode 100644 index 63d38d834c..0000000000 --- a/src/components/views/elements/ConfettiOverlay.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, {useEffect, useRef} from 'react'; -import {animateConfetti, forceStopConfetti} from './Confetti.js'; - -export default function ConfettiOverlay({roomWidth}) { - const canvasRef = useRef(null); - // on mount - useEffect(() => { - const resize = () => { - const canvas = canvasRef.current; - canvas.height = window.innerHeight; - }; - const canvas = canvasRef.current; - canvas.width = roomWidth; - canvas.height = window.innerHeight; - window.addEventListener("resize", resize, true); - animateConfetti(canvas, roomWidth); - return () => { - window.removeEventListener("resize", resize); - forceStopConfetti(); - }; - }, []); - // on roomWidth change - - useEffect(() => { - const canvas = canvasRef.current; - canvas.width = roomWidth; - }, [roomWidth]); - return ( - - ) -} \ No newline at end of file diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx new file mode 100644 index 0000000000..1f8e7a97ad --- /dev/null +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -0,0 +1,77 @@ +import React, {FunctionComponent, useEffect, useRef} from 'react'; +import dis from '../../../../dispatcher/dispatcher'; +import ICanvasEffect from './ICanvasEffect.js'; + +type EffectsOverlayProps = { + roomWidth: number; +} + +const EffectsOverlay: FunctionComponent = ({roomWidth}) => { + const canvasRef = useRef(null); + const effectsRef = useRef>(new Map()); + + const resize = () => { + canvasRef.current.height = window.innerHeight; + }; + + const lazyLoadEffectModule = async (name: string): Promise => { + if(!name) return null; + let effect = effectsRef.current[name] ?? null; + if(effect === null) { + try { + var { default: Effect } = await import(`./${name}`); + effect = new Effect(); + effectsRef.current[name] = effect; + } catch (err) { + console.warn('Unable to load effect module at \'./${name}\'.', err) + } + } + return effect; + } + + const onAction = (payload: { action: string }) => { + const actionPrefix = 'effects.'; + if(payload.action.indexOf(actionPrefix) === 0) { + const effect = payload.action.substr(actionPrefix.length); + lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); + } + }; + + // on mount + useEffect(() => { + const dispatcherRef = dis.register(onAction); + const canvas = canvasRef.current; + canvas.width = roomWidth; + canvas.height = window.innerHeight; + window.addEventListener('resize', resize, true); + + return () => { + dis.unregister(dispatcherRef); + window.removeEventListener('resize', resize); + for(const effect in effectsRef.current) { + effectsRef.current[effect]?.stop(); + } + }; + }, []); + + // on roomWidth change + useEffect(() => { + canvasRef.current.width = roomWidth; + }, [roomWidth]); + + return ( + + ) +} + +export default EffectsOverlay; \ No newline at end of file diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts new file mode 100644 index 0000000000..c463235880 --- /dev/null +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -0,0 +1,5 @@ +export default interface ICanvasEffect { + start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + stop: () => Promise, + isRunning: boolean +} \ No newline at end of file diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts new file mode 100644 index 0000000000..dd4e869078 --- /dev/null +++ b/src/components/views/elements/effects/confetti/index.ts @@ -0,0 +1,197 @@ +import ICanvasEffect from '../ICanvasEffect' + +declare global { + interface Window { + mozRequestAnimationFrame: any; + oRequestAnimationFrame: any; + msRequestAnimationFrame: any; + } +} + +export type ConfettiOptions = { + maxCount: number, + speed: number, + frameInterval: number, + alpha: number, + gradient: boolean, +} + +type ConfettiParticle = { + color: string, + color2: string, + x: number, + y: number, + diameter: number, + tilt: number, + tiltAngleIncrement: number, + tiltAngle: number, +} + +const DefaultOptions: ConfettiOptions = { + //set max confetti count + maxCount: 150, + //syarn addet the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + //use gradient instead of solid particle color + gradient: false, +}; + +export default class Confetti implements ICanvasEffect { + private readonly options: ConfettiOptions; + + constructor(options: ConfettiOptions = DefaultOptions) { + this.options = options; + } + + private context: CanvasRenderingContext2D | null; + private supportsAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame; + private colors = ['rgba(30,144,255,', 'rgba(107,142,35,', 'rgba(255,215,0,', + 'rgba(255,192,203,', 'rgba(106,90,205,', 'rgba(173,216,230,', + 'rgba(238,130,238,', 'rgba(152,251,152,', 'rgba(70,130,180,', + 'rgba(244,164,96,', 'rgba(210,105,30,', 'rgba(220,20,60,']; + + private lastFrameTime = Date.now(); + private particles: Array = []; + private waveAngle = 0; + + public isRunning: boolean; + + public start = async (canvas: HTMLCanvasElement, timeout?: number) => { + if(!canvas) { + return; + } + window.requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + return window.setTimeout(callback, this.options.frameInterval); + }; + })(); + if (this.context === null) { + this.context = canvas.getContext('2d'); + } + const count = this.options.maxCount; + while (this.particles.length < count) { + this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height)); + } + this.isRunning = true; + this.runAnimation(); + if (timeout) { + window.setTimeout(this.stop, timeout || 3000); + } + } + + public stop = async () => { + this.isRunning = false; + this.particles = []; + } + + private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { + particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + particle.x = Math.random() * width; + particle.y = Math.random() * height - height; + particle.diameter = Math.random() * 10 + 5; + particle.tilt = Math.random() * 10 - 10; + particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05; + particle.tiltAngle = Math.random() * Math.PI; + return particle; + } + + private runAnimation = (): void => { + if (this.particles.length === 0) { + this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); + //animationTimer = null; + } else { + const now = Date.now(); + const delta = now - this.lastFrameTime; + if (!this.supportsAnimationFrame || delta > this.options.frameInterval) { + this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); + this.updateParticles(); + this.drawParticles(this.context); + this.lastFrameTime = now - (delta % this.options.frameInterval); + } + requestAnimationFrame(this.runAnimation); + } + } + + + private drawParticles = (context: CanvasRenderingContext2D): void => { + let particle; + let x; let x2; let y2; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + this.context.beginPath(); + context.lineWidth = particle.diameter; + x2 = particle.x + particle.tilt; + x = x2 + particle.diameter / 2; + y2 = particle.y + particle.tilt + particle.diameter / 2; + if (this.options.gradient) { + const gradient = context.createLinearGradient(x, particle.y, x2, y2); + gradient.addColorStop(0, particle.color); + gradient.addColorStop(1.0, particle.color2); + context.strokeStyle = gradient; + } else { + context.strokeStyle = particle.color; + } + context.moveTo(x, particle.y); + context.lineTo(x2, y2); + context.stroke(); + } + } + + private updateParticles = () => { + const width = this.context.canvas.width; + const height = this.context.canvas.height; + let particle: ConfettiParticle; + this.waveAngle += 0.01; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + if (!this.isRunning && particle.y < -15) { + particle.y = height + 100; + } else { + particle.tiltAngle += particle.tiltAngleIncrement; + particle.x += Math.sin(this.waveAngle) - 0.5; + particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.options.speed) * 0.5; + particle.tilt = Math.sin(particle.tiltAngle) * 15; + } + if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { + if (this.isRunning && this.particles.length <= this.options.maxCount) { + this.resetParticle(particle, width, height); + } else { + this.particles.splice(i, 1); + i--; + } + } + } + } +} + +const convertToHex = (data: string): Array => { + const contentBodyToHexArray = []; + if (!data) return contentBodyToHexArray; + let hex; + if (data) { + for (let i = 0; i < data.length; i++) { + hex = data.codePointAt(i).toString(16); + contentBodyToHexArray.push(hex); + } + } + return contentBodyToHexArray; +} + +export const isConfettiEmoji = (content: { msgtype: string, body: string }): boolean => { + const hexArray = convertToHex(content.body); + return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); +} diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index d148d38b23..4fbea9d043 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,7 +42,7 @@ import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {isConfettiEmoji} from "../elements/Confetti"; +import {isConfettiEmoji} from "../elements/effects/confetti"; import SettingsStore from "../../../settings/SettingsStore"; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { @@ -318,10 +318,8 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (isConfettiEmoji(content)) { - dis.dispatch({action: 'confetti'}); - } + if (isConfettiEmoji(content)) { + dis.dispatch({action: 'effects.confetti'}); } } diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 95d0f4be46..078d4dd2c7 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -49,7 +49,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'showAvatarChanges', 'showDisplaynameChanges', 'showImages', - 'dontShowChatEffects', + 'showChatEffects', 'Pill.shouldShowPillAvatar', ]; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4fc7a3ad25..c3943eb764 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -510,7 +510,7 @@ "Manually verify all remote sessions": "Manually verify all remote sessions", "IRC display name width": "IRC display name width", "Enable experimental, compact IRC style layout": "Enable experimental, compact IRC style layout", - "Don't show chat effects": "Don't show chat effects", + "Show chat effects": "Show chat effects", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading logs": "Uploading logs", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b9ad834c83..ab4665c401 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -622,10 +622,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Enable experimental, compact IRC style layout"), default: false, }, - "dontShowChatEffects": { + "showChatEffects": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td("Don't show chat effects"), - default: false, + displayName: _td("Show chat effects"), + default: true, }, "Widgets.pinned": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, From 6d98335368b82ed6c7d5539bacfbd2f424fd8be7 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 21:28:22 +0200 Subject: [PATCH 0072/4306] Removed old todo --- src/components/structures/RoomView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 1b47386789..2c3aa4793a 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -824,7 +824,6 @@ export default class RoomView extends React.Component { this.calculateRecommendedVersion(room); this.updateE2EStatus(room); this.updatePermissions(room); - //TODO: forceStopConfetti(); }; private async calculateRecommendedVersion(room: Room) { From 48633f76ab92c5238a4f2e4c99f73d2598129f64 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 23:10:43 +0200 Subject: [PATCH 0073/4306] added event handling back --- src/SlashCommands.tsx | 2 +- src/components/structures/RoomView.tsx | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 316249d74d..68be5de0a0 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1047,7 +1047,7 @@ export const Commands = [ runFn: function(roomId, args) { return success((async () => { if (!args) { - args = _t("sends confetti"); + args = "sends confetti"; MatrixClientPeg.get().sendEmoteMessage(roomId, args); } else { MatrixClientPeg.get().sendTextMessage(roomId, args); diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 2c3aa4793a..f975a7cc6e 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,6 +73,7 @@ import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; +import { isConfettiEmoji } from '../views/elements/effects/confetti'; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -247,6 +248,8 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.on("Event.decrypted", this.onEventDecrypted); + this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate); @@ -567,6 +570,8 @@ export default class RoomView extends React.Component { this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.removeListener("Event.decrypted", this.onEventDecrypted); + this.context.removeListener("event", this.onEvent); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -795,6 +800,26 @@ export default class RoomView extends React.Component { } } }; + + private onEventDecrypted = (ev) => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || + this.state.room.getUnreadNotificationCount() === 0) return; + this.handleConfetti(ev); + }; + + private onEvent = (ev) => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; + this.handleConfetti(ev); + }; + + private handleConfetti = (ev) => { + if (this.state.matrixClientIsReady) { + const messageBody = 'sends confetti'; + if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { + dis.dispatch({action: 'effects.confetti'}); + } + } + }; private onRoomName = (room: Room) => { if (this.state.room && room.roomId == this.state.room.roomId) { From 1c6d28b861c8b6bee20c29056733ef08cc3dfa76 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Wed, 21 Oct 2020 13:37:36 +0200 Subject: [PATCH 0074/4306] refactoring roomView / slashCommands / SendMessageComposer with the new effects configurations and fix confetti animation timeout --- src/SlashCommands.tsx | 39 +++++++++++-------- src/components/structures/RoomView.tsx | 18 +++++---- .../views/elements/effects/ICanvasEffect.ts | 4 +- .../views/elements/effects/confetti/index.ts | 26 ++----------- .../views/elements/effects/effectUtilities.ts | 3 ++ .../views/elements/effects/index.ts | 11 ++++++ .../views/rooms/SendMessageComposer.js | 10 +++-- 7 files changed, 59 insertions(+), 52 deletions(-) create mode 100644 src/components/views/elements/effects/effectUtilities.ts create mode 100644 src/components/views/elements/effects/index.ts diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 68be5de0a0..3f51614028 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -46,6 +46,7 @@ import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from import SdkConfig from "./SdkConfig"; import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; +import effects from "./components/views/elements/effects" // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -1040,22 +1041,28 @@ export const Commands = [ }, category: CommandCategories.actions, }), - new Command({ - command: "confetti", - description: _td("Sends the given message with confetti"), - args: '', - runFn: function(roomId, args) { - return success((async () => { - if (!args) { - args = "sends confetti"; - MatrixClientPeg.get().sendEmoteMessage(roomId, args); - } else { - MatrixClientPeg.get().sendTextMessage(roomId, args); - } - dis.dispatch({action: 'effects.confetti'}); - })()); - }, - category: CommandCategories.effects, + ...effects.map((effect) => { + return new Command({ + command: effect.command, + description: effect.description(), + args: '', + runFn: function(roomId, args) { + return success((async () => { + if (!args) { + args = effect.fallbackMessage(); + MatrixClientPeg.get().sendEmoteMessage(roomId, args); + } else { + const content = { + msgtype: effect.msgType, + body: args, + }; + MatrixClientPeg.get().sendMessage(roomId, content); + } + dis.dispatch({action: `effects.${effect.command}`}); + })()); + }, + category: CommandCategories.effects, + }) }), // Command definitions for autocompletion ONLY: diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index f975a7cc6e..0b7aa08288 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,7 +73,8 @@ import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; -import { isConfettiEmoji } from '../views/elements/effects/confetti'; +import {containsEmoji} from '../views/elements/effects/effectUtilities'; +import effects from '../views/elements/effects' const DEBUG = false; let debuglog = function(msg: string) {}; @@ -800,10 +801,9 @@ export default class RoomView extends React.Component { } } }; - + private onEventDecrypted = (ev) => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; + if (ev.isDecryptionFailure()) return; this.handleConfetti(ev); }; @@ -813,11 +813,13 @@ export default class RoomView extends React.Component { }; private handleConfetti = (ev) => { + if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { - const messageBody = 'sends confetti'; - if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { - dis.dispatch({action: 'effects.confetti'}); - } + effects.map(effect => { + if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { + dis.dispatch({action: `effects.${effect.command}`}); + } + }) } }; diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index c463235880..a8b9a83514 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,5 +1,5 @@ export default interface ICanvasEffect { - start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + start: (canvas: HTMLCanvasElement, timeout: number) => Promise, stop: () => Promise, isRunning: boolean -} \ No newline at end of file +} diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index dd4e869078..c5874311c5 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -47,7 +47,7 @@ export default class Confetti implements ICanvasEffect { this.options = options; } - private context: CanvasRenderingContext2D | null; + private context: CanvasRenderingContext2D | null = null; private supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || @@ -64,7 +64,7 @@ export default class Confetti implements ICanvasEffect { public isRunning: boolean; - public start = async (canvas: HTMLCanvasElement, timeout?: number) => { + public start = async (canvas: HTMLCanvasElement, timeout = 3000) => { if(!canvas) { return; } @@ -88,13 +88,13 @@ export default class Confetti implements ICanvasEffect { this.isRunning = true; this.runAnimation(); if (timeout) { - window.setTimeout(this.stop, timeout || 3000); + window.setTimeout(this.stop, timeout); } } public stop = async () => { this.isRunning = false; - this.particles = []; + // this.particles = []; } private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { @@ -177,21 +177,3 @@ export default class Confetti implements ICanvasEffect { } } } - -const convertToHex = (data: string): Array => { - const contentBodyToHexArray = []; - if (!data) return contentBodyToHexArray; - let hex; - if (data) { - for (let i = 0; i < data.length; i++) { - hex = data.codePointAt(i).toString(16); - contentBodyToHexArray.push(hex); - } - } - return contentBodyToHexArray; -} - -export const isConfettiEmoji = (content: { msgtype: string, body: string }): boolean => { - const hexArray = convertToHex(content.body); - return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); -} diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts new file mode 100644 index 0000000000..927b445a61 --- /dev/null +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -0,0 +1,3 @@ +export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { + return emojis.some((emoji) => content.body.includes(emoji)); +} diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts new file mode 100644 index 0000000000..d4c12fa7ce --- /dev/null +++ b/src/components/views/elements/effects/index.ts @@ -0,0 +1,11 @@ +import {_t, _td} from "../../../../languageHandler"; + +export default [ + { + emojis: ['🎊', '🎉'], + msgType: 'nic.custom.confetti', + command: 'confetti', + description: () => _td("Sends the given message with confetti"), + fallbackMessage: () => _t("sends confetti") + " 🎉", + }, +] diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 4fbea9d043..94ad934067 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,8 +42,8 @@ import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {isConfettiEmoji} from "../elements/effects/confetti"; -import SettingsStore from "../../../settings/SettingsStore"; +import {containsEmoji} from "../elements/effects/effectUtilities"; +import effects from '../elements/effects'; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); @@ -318,9 +318,11 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - if (isConfettiEmoji(content)) { - dis.dispatch({action: 'effects.confetti'}); + effects.map( (effect) => { + if (containsEmoji(content, effect.emojis)) { + dis.dispatch({action: `effects.${effect.command}`}); } + }); } this.sendHistoryManager.save(this.model, replyToEvent); From d4ec1dd7750f5a901eaf079a7b5fd8153701aed3 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 13:56:58 +0200 Subject: [PATCH 0075/4306] Refactoring --- src/components/structures/RoomView.tsx | 2 +- .../views/elements/effects/confetti/index.ts | 1 - src/components/views/elements/effects/index.ts | 16 ++++++++++++++-- .../views/rooms/SendMessageComposer.js | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0b7aa08288..c84a3bf783 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -815,7 +815,7 @@ export default class RoomView extends React.Component { private handleConfetti = (ev) => { if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { - effects.map(effect => { + effects.forEach(effect => { if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { dis.dispatch({action: `effects.${effect.command}`}); } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index c5874311c5..e8a139387b 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -94,7 +94,6 @@ export default class Confetti implements ICanvasEffect { public stop = async () => { this.isRunning = false; - // this.particles = []; } private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index d4c12fa7ce..6311135c1e 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,6 +1,14 @@ import {_t, _td} from "../../../../languageHandler"; -export default [ +type Effect = { + emojis: Array; + msgType: string; + command: string; + description: () => string; + fallbackMessage: () => string; +} + +const effects: Array = [ { emojis: ['🎊', '🎉'], msgType: 'nic.custom.confetti', @@ -8,4 +16,8 @@ export default [ description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", }, -] +]; + +export default effects; + + diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 94ad934067..a413a9917c 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -318,10 +318,10 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - effects.map( (effect) => { + effects.forEach((effect) => { if (containsEmoji(content, effect.emojis)) { - dis.dispatch({action: `effects.${effect.command}`}); - } + dis.dispatch({action: `effects.${effect.command}`}); + } }); } From 047c29731b319bdf48027ae1e2966100763530f4 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:15:27 +0200 Subject: [PATCH 0076/4306] Added missing translation --- src/components/views/elements/effects/ICanvasEffect.ts | 2 +- src/i18n/strings/en_EN.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index a8b9a83514..71210d7ec3 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,5 +1,5 @@ export default interface ICanvasEffect { - start: (canvas: HTMLCanvasElement, timeout: number) => Promise, + start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, stop: () => Promise, isRunning: boolean } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c3943eb764..974658aa3d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -148,6 +148,7 @@ "Messages": "Messages", "Actions": "Actions", "Advanced": "Advanced", + "Effects": "Effects", "Other": "Other", "Command error": "Command error", "Usage": "Usage", @@ -211,8 +212,6 @@ "Send a bug report with logs": "Send a bug report with logs", "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", - "Sends the given message with confetti": "Sends the given message with confetti", - "sends confetti": "sends confetti", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", @@ -1580,6 +1579,8 @@ "Sign in with single sign-on": "Sign in with single sign-on", "And %(count)s more...|other": "And %(count)s more...", "Home": "Home", + "Sends the given message with confetti": "Sends the given message with confetti", + "sends confetti": "sends confetti", "Enter a server name": "Enter a server name", "Looks good": "Looks good", "Can't find this server or its room list": "Can't find this server or its room list", From c7d535d2d3d07ae6490365cb242d1f1e1f1d25a3 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:29:25 +0200 Subject: [PATCH 0077/4306] Fixed some formatting issues --- .../views/elements/effects/EffectsOverlay.tsx | 18 +++++++++--------- .../views/elements/effects/confetti/index.ts | 6 +++--- src/components/views/elements/effects/index.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 1f8e7a97ad..437d1f127f 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -1,4 +1,4 @@ -import React, {FunctionComponent, useEffect, useRef} from 'react'; +import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; import ICanvasEffect from './ICanvasEffect.js'; @@ -6,7 +6,7 @@ type EffectsOverlayProps = { roomWidth: number; } -const EffectsOverlay: FunctionComponent = ({roomWidth}) => { +const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { const canvasRef = useRef(null); const effectsRef = useRef>(new Map()); @@ -15,9 +15,9 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => }; const lazyLoadEffectModule = async (name: string): Promise => { - if(!name) return null; + if (!name) return null; let effect = effectsRef.current[name] ?? null; - if(effect === null) { + if (effect === null) { try { var { default: Effect } = await import(`./${name}`); effect = new Effect(); @@ -31,7 +31,7 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => const onAction = (payload: { action: string }) => { const actionPrefix = 'effects.'; - if(payload.action.indexOf(actionPrefix) === 0) { + if (payload.action.indexOf(actionPrefix) === 0) { const effect = payload.action.substr(actionPrefix.length); lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); } @@ -44,11 +44,11 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => canvas.width = roomWidth; canvas.height = window.innerHeight; window.addEventListener('resize', resize, true); - - return () => { + + return () => { dis.unregister(dispatcherRef); window.removeEventListener('resize', resize); - for(const effect in effectsRef.current) { + for (const effect in effectsRef.current) { effectsRef.current[effect]?.stop(); } }; @@ -58,7 +58,7 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => useEffect(() => { canvasRef.current.width = roomWidth; }, [roomWidth]); - + return ( { - if(!canvas) { + if (!canvas) { return; } - window.requestAnimationFrame = (function () { + window.requestAnimationFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { + function(callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 6311135c1e..8a95b1c9d0 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,4 +1,4 @@ -import {_t, _td} from "../../../../languageHandler"; +import { _t, _td } from "../../../../languageHandler"; type Effect = { emojis: Array; From 8728e12242d0a751407e9a2c08c95ecaa5844dfd Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:43:09 +0200 Subject: [PATCH 0078/4306] Some code optimizations --- .../views/elements/effects/EffectsOverlay.tsx | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 437d1f127f..4b40f7cbb1 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -10,16 +10,12 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = const canvasRef = useRef(null); const effectsRef = useRef>(new Map()); - const resize = () => { - canvasRef.current.height = window.innerHeight; - }; - const lazyLoadEffectModule = async (name: string): Promise => { if (!name) return null; let effect = effectsRef.current[name] ?? null; if (effect === null) { try { - var { default: Effect } = await import(`./${name}`); + const { default: Effect } = await import(`./${name}`); effect = new Effect(); effectsRef.current[name] = effect; } catch (err) { @@ -27,41 +23,41 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = } } return effect; - } - - const onAction = (payload: { action: string }) => { - const actionPrefix = 'effects.'; - if (payload.action.indexOf(actionPrefix) === 0) { - const effect = payload.action.substr(actionPrefix.length); - lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); - } }; - // on mount useEffect(() => { + const resize = () => { + canvasRef.current.height = window.innerHeight; + }; + const onAction = (payload: { action: string }) => { + const actionPrefix = 'effects.'; + if (payload.action.indexOf(actionPrefix) === 0) { + const effect = payload.action.substr(actionPrefix.length); + lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); + } + } const dispatcherRef = dis.register(onAction); const canvas = canvasRef.current; - canvas.width = roomWidth; canvas.height = window.innerHeight; window.addEventListener('resize', resize, true); return () => { dis.unregister(dispatcherRef); window.removeEventListener('resize', resize); - for (const effect in effectsRef.current) { - effectsRef.current[effect]?.stop(); + const currentEffects = effectsRef.current; + for (const effect in currentEffects) { + const effectModule: ICanvasEffect = currentEffects[effect]; + if(effectModule && effectModule.isRunning) { + effectModule.stop(); + } } }; }, []); - // on roomWidth change - useEffect(() => { - canvasRef.current.width = roomWidth; - }, [roomWidth]); - return ( = ({ roomWidth }) = ) } -export default EffectsOverlay; \ No newline at end of file +export default EffectsOverlay; From 88475617955c72a538269259a448f54f8e5e27fd Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:48:11 +0200 Subject: [PATCH 0079/4306] Added additional module exports --- src/components/views/elements/effects/EffectsOverlay.tsx | 2 +- src/components/views/elements/effects/confetti/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 4b40f7cbb1..0ff2b228ad 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -2,7 +2,7 @@ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; import ICanvasEffect from './ICanvasEffect.js'; -type EffectsOverlayProps = { +export type EffectsOverlayProps = { roomWidth: number; } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 3cb7db5ec4..e45961006b 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -27,7 +27,7 @@ type ConfettiParticle = { tiltAngle: number, } -const DefaultOptions: ConfettiOptions = { +export const DefaultOptions: ConfettiOptions = { //set max confetti count maxCount: 150, //syarn addet the particle animation speed From 6f4c5d1b080f8ddcbd6053f6778f52d6f15f2125 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:56:04 +0200 Subject: [PATCH 0080/4306] fixed build error --- src/components/views/elements/effects/EffectsOverlay.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 0ff2b228ad..803fd18042 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -43,11 +43,12 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = return () => { dis.unregister(dispatcherRef); - window.removeEventListener('resize', resize); - const currentEffects = effectsRef.current; + window.removeEventListener('resize', resize); + // eslint-disable-next-line react-hooks/exhaustive-deps + const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored for (const effect in currentEffects) { const effectModule: ICanvasEffect = currentEffects[effect]; - if(effectModule && effectModule.isRunning) { + if (effectModule && effectModule.isRunning) { effectModule.stop(); } } From 906686b640ff22812f3cb628bd3f8267af6c8144 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 15:06:05 +0200 Subject: [PATCH 0081/4306] Fixed more linter warnings --- src/components/views/elements/effects/confetti/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index e45961006b..4537683030 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -68,13 +68,13 @@ export default class Confetti implements ICanvasEffect { if (!canvas) { return; } - window.requestAnimationFrame = (function() { + window.requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function(callback) { + function (callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); From 2f83771eab27a2e6441a34224c9acbd120c6c5b8 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 15:15:26 +0200 Subject: [PATCH 0082/4306] Fixed more eslint errors --- src/components/views/elements/effects/EffectsOverlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 803fd18042..5ec3566f18 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -43,7 +43,7 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = return () => { dis.unregister(dispatcherRef); - window.removeEventListener('resize', resize); + window.removeEventListener('resize', resize); // eslint-disable-next-line react-hooks/exhaustive-deps const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored for (const effect in currentEffects) { From 335774b6ff3bd558f11eea9ca58e2f1d7f73c262 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 16:03:22 +0200 Subject: [PATCH 0083/4306] Fixed more linter errors --- src/components/views/elements/effects/confetti/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 4537683030..7428651490 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -1,4 +1,4 @@ -import ICanvasEffect from '../ICanvasEffect' +import ICanvasEffect from '../ICanvasEffect'; declare global { interface Window { @@ -68,13 +68,13 @@ export default class Confetti implements ICanvasEffect { if (!canvas) { return; } - window.requestAnimationFrame = (function () { + window.requestAnimationFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { + function(callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); From fbe2d7e0f86ceae6511b6dc553a70d428b773290 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 16:15:15 +0200 Subject: [PATCH 0084/4306] Optimized naming --- src/components/structures/RoomView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 817b2d2cea..1a18ece008 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -822,15 +822,15 @@ export default class RoomView extends React.Component { private onEventDecrypted = (ev) => { if (ev.isDecryptionFailure()) return; - this.handleConfetti(ev); + this.handleEffects(ev); }; private onEvent = (ev) => { if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); + this.handleEffects(ev); }; - private handleConfetti = (ev) => { + private handleEffects = (ev) => { if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { effects.forEach(effect => { From cb79e38377165b4cb233caa804fadc9272e5e2a8 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 17:04:01 +0200 Subject: [PATCH 0085/4306] Better initialization and check if canvas gets unmounted --- .../views/elements/effects/confetti/index.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 7428651490..b613c32043 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -78,9 +78,8 @@ export default class Confetti implements ICanvasEffect { return window.setTimeout(callback, this.options.frameInterval); }; })(); - if (this.context === null) { - this.context = canvas.getContext('2d'); - } + this.context = canvas.getContext('2d'); + this.particles = []; const count = this.options.maxCount; while (this.particles.length < count) { this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height)); @@ -109,9 +108,11 @@ export default class Confetti implements ICanvasEffect { } private runAnimation = (): void => { + if (!this.context || !this.context.canvas) { + return; + } if (this.particles.length === 0) { this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); - //animationTimer = null; } else { const now = Date.now(); const delta = now - this.lastFrameTime; @@ -127,6 +128,9 @@ export default class Confetti implements ICanvasEffect { private drawParticles = (context: CanvasRenderingContext2D): void => { + if (!this.context || !this.context.canvas) { + return; + } let particle; let x; let x2; let y2; for (let i = 0; i < this.particles.length; i++) { @@ -151,6 +155,9 @@ export default class Confetti implements ICanvasEffect { } private updateParticles = () => { + if (!this.context || !this.context.canvas) { + return; + } const width = this.context.canvas.width; const height = this.context.canvas.height; let particle: ConfettiParticle; From 3ea4560019f111c5ce73382644d6d6aae9fb753a Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 17:58:54 +0200 Subject: [PATCH 0086/4306] Moved effect options to configuration --- .../views/elements/effects/EffectsOverlay.tsx | 12 +++++---- .../views/elements/effects/ICanvasEffect.ts | 4 +++ .../views/elements/effects/confetti/index.ts | 4 +-- .../views/elements/effects/index.ts | 25 ++++++++++++++++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 5ec3566f18..b2ecec8753 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -1,6 +1,7 @@ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; -import ICanvasEffect from './ICanvasEffect.js'; +import ICanvasEffect, { ICanvasEffectConstructable } from './ICanvasEffect.js'; +import effects from './index' export type EffectsOverlayProps = { roomWidth: number; @@ -8,15 +9,16 @@ export type EffectsOverlayProps = { const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { const canvasRef = useRef(null); - const effectsRef = useRef>(new Map()); + const effectsRef = useRef>(new Map()); const lazyLoadEffectModule = async (name: string): Promise => { if (!name) return null; - let effect = effectsRef.current[name] ?? null; + let effect: ICanvasEffect | null = effectsRef.current[name] || null; if (effect === null) { + const options = effects.find((e) => e.command === name)?.options try { - const { default: Effect } = await import(`./${name}`); - effect = new Effect(); + const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`./${name}`); + effect = new Effect(options); effectsRef.current[name] = effect; } catch (err) { console.warn('Unable to load effect module at \'./${name}\'.', err) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index 71210d7ec3..c2a3046c8f 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,3 +1,7 @@ +export interface ICanvasEffectConstructable { + new(options?: { [key: string]: any }): ICanvasEffect +} + export default interface ICanvasEffect { start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, stop: () => Promise, diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index b613c32043..07b6f1632a 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -43,8 +43,8 @@ export const DefaultOptions: ConfettiOptions = { export default class Confetti implements ICanvasEffect { private readonly options: ConfettiOptions; - constructor(options: ConfettiOptions = DefaultOptions) { - this.options = options; + constructor(options: { [key: string]: any }) { + this.options = {...DefaultOptions, ...options}; } private context: CanvasRenderingContext2D | null = null; diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 8a95b1c9d0..3986d6e841 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,11 +1,22 @@ import { _t, _td } from "../../../../languageHandler"; -type Effect = { +export type Effect = { emojis: Array; msgType: string; command: string; description: () => string; fallbackMessage: () => string; + options: { + [key: string]: any + } +} + +type ConfettiOptions = { + maxCount: number, + speed: number, + frameInterval: number, + alpha: number, + gradient: boolean, } const effects: Array = [ @@ -15,6 +26,18 @@ const effects: Array = [ command: 'confetti', description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", + options: { + //set max confetti count + maxCount: 150, + //syarn addet the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + //use gradient instead of solid particle color + gradient: false, + } as ConfettiOptions, }, ]; From 1c556c97d3a23d95389ef3f1ce7be642a1885195 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 19:06:10 +0200 Subject: [PATCH 0087/4306] Added some code documentation --- .../views/elements/effects/ICanvasEffect.ts | 20 +++++++ .../views/elements/effects/confetti/index.ts | 20 +++++-- .../views/elements/effects/effectUtilities.ts | 5 ++ .../views/elements/effects/index.ts | 53 ++++++++++++++----- 4 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index c2a3046c8f..400f42af73 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,9 +1,29 @@ +/** + * Defines the constructor of a canvas based room effect + */ export interface ICanvasEffectConstructable { + /** + * @param {{[key:string]:any}} options? Optional animation options + * @returns ICanvasEffect Returns a new instance of the canvas effect + */ new(options?: { [key: string]: any }): ICanvasEffect } +/** + * Defines the interface of a canvas based room effect + */ export default interface ICanvasEffect { + /** + * @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation + * @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false) + */ start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + /** + * Stops the current animation + */ stop: () => Promise, + /** + * Returns a value that defines if the animation is currently running + */ isRunning: boolean } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 07b6f1632a..29f70d1a57 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -9,10 +9,25 @@ declare global { } export type ConfettiOptions = { + /** + * max confetti count + */ maxCount: number, + /** + * particle animation speed + */ speed: number, + /** + * the confetti animation frame interval in milliseconds + */ frameInterval: number, + /** + * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + */ alpha: number, + /** + * use gradient instead of solid particle color + */ gradient: boolean, } @@ -28,15 +43,10 @@ type ConfettiParticle = { } export const DefaultOptions: ConfettiOptions = { - //set max confetti count maxCount: 150, - //syarn addet the particle animation speed speed: 3, - //the confetti animation frame interval in milliseconds frameInterval: 15, - //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) alpha: 1.0, - //use gradient instead of solid particle color gradient: false, }; diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts index 927b445a61..212c477b39 100644 --- a/src/components/views/elements/effects/effectUtilities.ts +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -1,3 +1,8 @@ +/** + * Checks a message if it contains one of the provided emojis + * @param {Object} content The message + * @param {Array} emojis The list of emojis to check for + */ export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { return emojis.some((emoji) => content.body.includes(emoji)); } diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 3986d6e841..0f01f2624e 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,25 +1,59 @@ import { _t, _td } from "../../../../languageHandler"; -export type Effect = { +export type Effect = { + /** + * one or more emojis that will trigger this effect + */ emojis: Array; + /** + * the matrix message type that will trigger this effect + */ msgType: string; + /** + * the room command to trigger this effect + */ command: string; + /** + * a function that returns the translated description of the effect + */ description: () => string; + /** + * a function that returns the translated fallback message. this message will be shown if the user did not provide a custom message + */ fallbackMessage: () => string; - options: { - [key: string]: any - } + /** + * animation options + */ + options: TOptions; } type ConfettiOptions = { + /** + * max confetti count + */ maxCount: number, + /** + * particle animation speed + */ speed: number, + /** + * the confetti animation frame interval in milliseconds + */ frameInterval: number, + /** + * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + */ alpha: number, + /** + * use gradient instead of solid particle color + */ gradient: boolean, } -const effects: Array = [ +/** + * This configuration defines room effects that can be triggered by custom message types and emojis + */ +const effects: Array> = [ { emojis: ['🎊', '🎉'], msgType: 'nic.custom.confetti', @@ -27,18 +61,13 @@ const effects: Array = [ description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", options: { - //set max confetti count maxCount: 150, - //syarn addet the particle animation speed speed: 3, - //the confetti animation frame interval in milliseconds frameInterval: 15, - //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) alpha: 1.0, - //use gradient instead of solid particle color gradient: false, - } as ConfettiOptions, - }, + }, + } as Effect, ]; export default effects; From c5c0617aee3ff73295ef4e9da41da534a6c1b1c8 Mon Sep 17 00:00:00 2001 From: aethralis Date: Wed, 21 Oct 2020 17:39:06 +0000 Subject: [PATCH 0088/4306] Translated using Weblate (Estonian) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index b4531b8ee7..c782665da9 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -2538,5 +2538,14 @@ "The call could not be established": "Kõnet ei saa korraldada", "%(senderName)s declined the call.": "%(senderName)s ei võtnud kõnet vastu.", "The other party declined the call.": "Teine osapool ei võtnud kõnet vastu.", - "Call Declined": "Kõne on tagasilükatud" + "Call Declined": "Kõne on tagasilükatud", + "Move right": "Liigu paremale", + "Move left": "Liigu vasakule", + "Revoke permissions": "Tühista load", + "Unpin a widget to view it in this panel": "Sellel paneelil kuvamiseks eemaldage vidin lemmikutest", + "You can only pin up to %(count)s widgets|other": "Saab kinnitada ainult kuni %(count)s vidinaid", + "Show Widgets": "Kuva vidinad", + "Hide Widgets": "Peida vidinad", + "The call was answered on another device.": "Kõnele vastati teises seadmes.", + "Answered Elsewhere": "Vastatud mujal" } From 46eb5cdb1b036f2e696cd97fa9540c8ff18285be Mon Sep 17 00:00:00 2001 From: MaHa-Nordeck Date: Thu, 22 Oct 2020 14:01:16 +0200 Subject: [PATCH 0089/4306] Minor improvements * Made color generation dependant on gradient usage. * Changed a loop to use for of --- .../views/elements/effects/confetti/index.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 29f70d1a57..309fc9dd9c 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -107,11 +107,15 @@ export default class Confetti implements ICanvasEffect { private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); - particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + if(this.options.gradient) { + particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + } else { + particle.color2 = particle.color; + } particle.x = Math.random() * width; - particle.y = Math.random() * height - height; + particle.y = Math.random() * -height; particle.diameter = Math.random() * 10 + 5; - particle.tilt = Math.random() * 10 - 10; + particle.tilt = Math.random() * -10; particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05; particle.tiltAngle = Math.random() * Math.PI; return particle; @@ -141,10 +145,8 @@ export default class Confetti implements ICanvasEffect { if (!this.context || !this.context.canvas) { return; } - let particle; let x; let x2; let y2; - for (let i = 0; i < this.particles.length; i++) { - particle = this.particles[i]; + for (const particle of this.particles) { this.context.beginPath(); context.lineWidth = particle.diameter; x2 = particle.x + particle.tilt; From 674060ed9373b1a2ca210449e4a4110415ce21ba Mon Sep 17 00:00:00 2001 From: MaHa-Nordeck Date: Thu, 22 Oct 2020 15:28:30 +0200 Subject: [PATCH 0090/4306] fixed spacing --- src/components/views/elements/effects/confetti/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 309fc9dd9c..aee8f54a3a 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -107,7 +107,7 @@ export default class Confetti implements ICanvasEffect { private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); - if(this.options.gradient) { + if (this.options.gradient) { particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); } else { particle.color2 = particle.color; From bfaab7591da95c8c407c3ada148a917f47fa4a00 Mon Sep 17 00:00:00 2001 From: Francesco Date: Fri, 23 Oct 2020 09:51:06 +0000 Subject: [PATCH 0091/4306] Translated using Weblate (Italian) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-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 d6f7cc714e..f4b6ee6bed 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2540,5 +2540,6 @@ "(connection failed)": "(connessione fallita)", "The call could not be established": "Impossibile stabilire la chiamata", "The other party declined the call.": "Il destinatario ha rifiutato la chiamata.", - "Call Declined": "Chiamata rifiutata" + "Call Declined": "Chiamata rifiutata", + "Offline encrypted messaging using dehydrated devices": "Messaggistica offline criptata usando dispositivi \"deidratati\"" } From 5cd1a28d131ded53babcea765f12d8c2aace4c5e Mon Sep 17 00:00:00 2001 From: random Date: Wed, 21 Oct 2020 13:08:44 +0000 Subject: [PATCH 0092/4306] Translated using Weblate (Italian) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index f4b6ee6bed..abb8ccbedc 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2541,5 +2541,14 @@ "The call could not be established": "Impossibile stabilire la chiamata", "The other party declined the call.": "Il destinatario ha rifiutato la chiamata.", "Call Declined": "Chiamata rifiutata", - "Offline encrypted messaging using dehydrated devices": "Messaggistica offline criptata usando dispositivi \"deidratati\"" + "Offline encrypted messaging using dehydrated devices": "Messaggistica offline criptata usando dispositivi \"deidratati\"", + "Move right": "Sposta a destra", + "Move left": "Sposta a sinistra", + "Revoke permissions": "Revoca autorizzazioni", + "Unpin a widget to view it in this panel": "Sblocca un widget per vederlo in questo pannello", + "You can only pin up to %(count)s widgets|other": "Puoi ancorare al massimo %(count)s widget", + "Show Widgets": "Mostra i widget", + "Hide Widgets": "Nascondi i widget", + "The call was answered on another device.": "La chiamata è stata accettata su un altro dispositivo.", + "Answered Elsewhere": "Risposto altrove" } From d17d2e8af4c951fffedb291132fcc919503158fc Mon Sep 17 00:00:00 2001 From: ProfP30 Date: Thu, 22 Oct 2020 12:29:43 +0000 Subject: [PATCH 0093/4306] Translated using Weblate (German) Currently translated at 99.9% (2364 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-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 07eb46e40b..b4404f653c 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1218,7 +1218,7 @@ "Send %(eventType)s events": "Sende %(eventType)s-Ereignisse", "Select the roles required to change various parts of the room": "Wähle Rollen die benötigt werden um einige Teile des Raumes zu ändern", "Enable encryption?": "Verschlüsselung aktivieren?", - "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.": "Sobald aktiviert, kann die Verschlüsselung für einen Raum nicht mehr deaktiviert werden. Nachrichten in einem verschlüsselten Raum können nur noch von Teilnehmern aber nicht mehr vom Server gelesen werden. Einige Bots und Brücken werden vielleicht nicht mehr funktionieren. Lerne mehr über Verschlüsselung", + "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.": "Sobald aktiviert, kann die Verschlüsselung für einen Raum nicht mehr deaktiviert werden. Nachrichten in einem verschlüsselten Raum können nur noch von Teilnehmern aber nicht mehr vom Server gelesen werden. Einige Bots und Brücken werden vielleicht nicht mehr funktionieren. Erfahre mehr über Verschlüsselung.", "Error updating main address": "Fehler beim Aktualisieren der Hauptadresse", "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Es gab ein Problem beim Aktualisieren der Raum-Hauptadresse. Es kann sein, dass es vom Server verboten ist oder ein temporäres Problem auftrat.", "Error updating flair": "Konnte Abzeichen nicht aktualisieren", From 5c49777e08aaff3d64dce70a2a5f73b3abfe756d Mon Sep 17 00:00:00 2001 From: "@a2sc:matrix.org" Date: Wed, 21 Oct 2020 18:16:16 +0000 Subject: [PATCH 0094/4306] Translated using Weblate (German) Currently translated at 99.9% (2364 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index b4404f653c..b82fe80963 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -809,7 +809,7 @@ "Terms and Conditions": "Geschäftsbedingungen", "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Um den %(homeserverDomain)s -Heimserver weiter zu verwenden, musst du die Geschäftsbedingungen sichten und ihnen zustimmen.", "Review terms and conditions": "Geschäftsbedingungen anzeigen", - "Share Link to User": "Sende Link an Benutzer", + "Share Link to User": "Link zum Benutzer teilen", "Share room": "Teile Raum", "Share Room": "Teile Raum", "Link to most recent message": "Link zur aktuellsten Nachricht", @@ -2525,5 +2525,24 @@ "Starting camera...": "Starte Kamera...", "Call connecting...": "Verbinde den Anruf...", "Calling...": "Rufe an...", - "Starting microphone...": "Starte Mikrofon..." + "Starting microphone...": "Starte Mikrofon...", + "Move right": "Nach rechts schieben", + "Move left": "Nach links schieben", + "Revoke permissions": "Berechtigungen widerrufen", + "Unpin a widget to view it in this panel": "Widget nicht mehr anheften, um es in diesem Bereich anzuzeigen", + "You can only pin up to %(count)s widgets|other": "Du kannst nur bis zu %(count)s Widgets anheften", + "Show Widgets": "Widgets anzeigen", + "Hide Widgets": "Widgets verstecken", + "%(senderName)s declined the call.": "%(senderName)s hat den Anruf abgelehnt.", + "(an error occurred)": "(ein Fehler ist aufgetreten)", + "(their device couldn't start the camera / microphone)": "(ihr/sein Gerät konnte Kamera / Mikrophon nicht starten)", + "(connection failed)": "(Verbindung fehlgeschlagen)", + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Alle Server sind von der Teilnahme ausgeschlossen! Dieser Raum kann nicht mehr genutzt werden.", + "%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s hat die Server-ACLs für diesen Raum geändert.", + "%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s hat die Server-ACLs für diesen Raum gesetzt.", + "The call was answered on another device.": "Der Anruf wurde an einem anderen Gerät angenommen.", + "Answered Elsewhere": "Anderswo beantwortet", + "The call could not be established": "Der Anruf konnte nicht hergestellt werden", + "The other party declined the call.": "Die andere Seite hat den Anruf abgelehnt.", + "Call Declined": "Anruf abgelehnt" } From 94aa324b030f38e5423cc439d50ff4af567b98f6 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 23 Oct 2020 00:20:40 +0000 Subject: [PATCH 0095/4306] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index f7dc067a1a..2cae187676 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -2469,5 +2469,14 @@ "(an error occurred)": "(ocorreu um erro)", "(connection failed)": "(a conexão falhou)", "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Todos os servidores foram banidos desta sala! Esta sala não pode mais ser utilizada.", - "Call Declined": "Chamada recusada" + "Call Declined": "Chamada recusada", + "You can only pin up to %(count)s widgets|other": "Você pode fixar até %(count)s widgets", + "Move right": "Mover para a direita", + "Move left": "Mover para a esquerda", + "Revoke permissions": "Revogar permissões", + "Unpin a widget to view it in this panel": "Desafixe um widget para exibi-lo neste painel", + "Show Widgets": "Mostrar widgets", + "Hide Widgets": "Esconder widgets", + "The call was answered on another device.": "A chamada foi atendida em outro aparelho.", + "Answered Elsewhere": "Atendida em algum lugar" } From 0639be3d00be402001a164a155d01efdc02f02aa Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 22 Oct 2020 03:01:37 +0000 Subject: [PATCH 0096/4306] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 0ae9b7edb0..c6c800b142 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2541,5 +2541,14 @@ "(connection failed)": "(連線失敗)", "The call could not be established": "無法建立通話", "The other party declined the call.": "對方拒絕了電話。", - "Call Declined": "通話已拒絕" + "Call Declined": "通話已拒絕", + "Move right": "向右移動", + "Move left": "向左移動", + "Revoke permissions": "撤銷權限", + "Unpin a widget to view it in this panel": "取消釘選小工具以在此面板檢視", + "You can only pin up to %(count)s widgets|other": "您最多只能釘選 %(count)s 個小工具", + "Show Widgets": "顯示小工具", + "Hide Widgets": "隱藏小工具", + "The call was answered on another device.": "通話已在其他裝置上回應。", + "Answered Elsewhere": "在其他地方回答" } From 1c66a9981fccad8a7c522b18ba9cd425fd7a1fee Mon Sep 17 00:00:00 2001 From: Tirifto Date: Thu, 22 Oct 2020 20:06:18 +0000 Subject: [PATCH 0097/4306] Translated using Weblate (Esperanto) Currently translated at 99.7% (2359 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 121 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index aec575b8b4..a3a4ece6b3 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1939,7 +1939,7 @@ "Incorrect recovery passphrase": "Malĝusta rehava pasfrazo", "Enter recovery passphrase": "Enigu la rehavan pasfrazon", "Enter recovery key": "Enigu la rehavan ŝlosilon", - "Warning: You should only set up key backup from a trusted computer.": "Averto: savkopiadon de ŝlosiloj vi starigu nur el fidata komputilo.", + "Warning: You should only set up key backup from a trusted computer.": "Averto: savkopiadon de ŝlosiloj vi starigu nur per fidata komputilo.", "If you've forgotten your recovery key you can ": "Se vi forgesis vian rehavan ŝlosilon, vi povas ", "Reload": "Relegi", "Remove for everyone": "Forigi por ĉiuj", @@ -2407,5 +2407,122 @@ "Recent changes that have not yet been received": "Freŝaj ŝanĝoj ankoraŭ ne ricevitaj", "No files visible in this room": "Neniuj dosieroj videblas en ĉi tiu ĉambro", "You have no visible notifications in this room.": "Vi havas neniujn videblajn sciigojn en ĉi tiu ĉambro.", - "%(brand)s Android": "%(brand)s por Android" + "%(brand)s Android": "%(brand)s por Android", + "Community and user menu": "Menuo de komunumo kaj uzanto", + "User settings": "Agordoj de uzanto", + "Community settings": "Agordoj de komunumo", + "Failed to find the general chat for this community": "Malsukcesis trovi la ĝeneralan babilejon por ĉi tiu komunumo", + "Starting camera...": "Pretigante filmilon…", + "Starting microphone...": "Pretigante mikrofonon…", + "Call connecting...": "Konektante vokon…", + "Calling...": "Vokante…", + "Explore rooms in %(communityName)s": "Esploru ĉambrojn en %(communityName)s", + "You do not have permission to create rooms in this community.": "Vi ne havas permeson krei ĉambrojn en ĉi tiu komunumo.", + "Cannot create rooms in this community": "Ne povas krei ĉambrojn en ĉi tiu komunumo", + "Create community": "Krei komunumon", + "Attach files from chat or just drag and drop them anywhere in a room.": "Kunsendu dosierojn per la babilujo, aŭ trenu ilin kien ajn en ĉambro vi volas.", + "Enter the location of your Element Matrix Services homeserver. It may use your own domain name or be a subdomain of element.io.": "Enigu la lokon de via hejmservilo de «Element Matrix Services». Ĝi povas uzi vian propran retnomon aŭ esti subretnomo de element.io.", + "Move right": "Movi dekstren", + "Move left": "Movi maldekstren", + "Revoke permissions": "Nuligi permesojn", + "Take a picture": "Foti", + "Unable to set up keys": "Ne povas agordi ŝlosilojn", + "You're all caught up.": "Sen sciigoj.", + "Invite someone using their name, username (like ) or share this room.": "Invitu iun per ĝia nomo, uzantonomo (kiel ), aŭ diskonigu la ĉambron.", + "This won't invite them to %(communityName)s. To invite someone to %(communityName)s, click here": "Ĉi tio ne invitos ĝin al %(communityName)s. Por inviti iun al %(communityName)s, klaku ĉi tien", + "Start a conversation with someone using their name or username (like ).": "Komencu interparolon kun iu per ĝia nomo aŭ uzantonomo (kiel ).", + "May include members not in %(communityName)s": "Povas inkluzivi anojn ekster %(communityName)s", + "Update community": "Ĝisdatigi komunumon", + "There was an error updating your community. The server is unable to process your request.": "Eraris ĝisdatigo de via komunumo. La servilo ne povas trakti vian peton.", + "Block anyone not part of %(serverName)s from ever joining this room.": "Bloki de la ĉambro ĉiun ekster %(serverName)s.", + "Create a room in %(communityName)s": "Krei ĉambron en %(communityName)s", + "You might disable this if the room will be used for collaborating with external teams who have their own homeserver. This cannot be changed later.": "Vi povas malŝalti ĉi tion se la ĉambro estos uzata por kunlaborado kun eksteraj skipoj, kun iliaj propraj hejmserviloj. Ĝi ne povas ŝanĝiĝi poste.", + "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.": "Vi povus ŝalti ĉi tion se la ĉambro estus uzota nur por kunlaborado de internaj skipoj je via hejmservilo. Ĝi ne ŝanĝeblas poste.", + "Your server requires encryption to be enabled in private rooms.": "Via servilo postulas ŝaltitan ĉifradon en privataj ĉambroj.", + "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Privataj ĉambroj povas esti trovitaj kaj aliĝitaj nur per invito. Publikaj ĉambroj povas esti trovitaj kaj aliĝitaj de iu ajn en ĉi tiu komunumo.", + "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone.": "Privataj ĉambroj povas esti trovitaj kaj aliĝitaj nur per invito. Publikaj ĉambroj povas esti trovitaj kaj aliĝitaj de iu ajn.", + "An image will help people identify your community.": "Bildo helpos al aliuloj rekoni vian komunumon.", + "Add image (optional)": "Aldonu bildon (se vi volas)", + "Enter name": "Enigu nomon", + "What's the name of your community or team?": "Kio estas la nomo de via komunumo aŭ skipo?", + "You can change this later if needed.": "Vi povas ŝanĝi ĉi tion poste, laŭbezone.", + "Use this when referencing your community to others. The community ID cannot be changed.": "Uzu ĉi tion kiam vi montras vian komunumon al aliuloj. La identigilo ne povas ŝanĝiĝi.", + "Community ID: +:%(domain)s": "Identigilo de komunumo: +:%(domain)s", + "There was an error creating your community. The name may be taken or the server is unable to process your request.": "Eraris kreado de via komunumo. Eble la nomo jam estas prenita, aŭ la servilo ne povas trakti vian peton.", + "Invite people to join %(communityName)s": "Inviti personojn al komunumo %(communityName)s", + "Send %(count)s invites|one": "Sendi %(count)s inviton", + "Send %(count)s invites|other": "Sendi %(count)s invitojn", + "People you know on %(brand)s": "Personoj, kiujn vi scias je %(brand)s", + "Add another email": "Aldoni alian retpoŝtadreson", + "Preparing to download logs": "Preparante elŝuton de protokolo", + "Download logs": "Elŝuti protokolon", + "Information": "Informoj", + "This version of %(brand)s does not support searching encrypted messages": "Ĉi tiu versio de %(brand)s ne subtenas serĉadon de ĉifritaj mesaĝoj", + "This version of %(brand)s does not support viewing some encrypted files": "Ĉi tiu versio de %(brand)s ne subtenas montradon de iuj ĉifritaj dosieroj", + "Use the Desktop app to search encrypted messages": "Uzu la labortablan aplikaĵon por serĉi ĉifritajn mesaĝojn", + "Use the Desktop app to see all encrypted files": "Uzu la labortablan aplikaĵon por vidi ĉiujn ĉifritajn dosierojn", + "Video conference started by %(senderName)s": "Grupan vidvokon komencis %(senderName)s", + "Video conference updated by %(senderName)s": "Grupan vidvokon ĝisdatigis %(senderName)s", + "Video conference ended by %(senderName)s": "Grupan vidvokon finis %(senderName)s", + "Join the conference from the room information card on the right": "Aliĝu al la grupa voko per la dekstraflanka karto kun informoj pri ĉambro", + "Join the conference at the top of this room": "Aliĝu al la grupa voko supre je la ĉambro", + "Ignored attempt to disable encryption": "Malatentis provon malŝalti ĉifradon", + "Room settings": "Agordoj de ĉambro", + "Show files": "Montri dosierojn", + "%(count)s people|one": "%(count)s persono", + "%(count)s people|other": "%(count)s personoj", + "About": "Prio", + "Not encrypted": "Neĉifrita", + "Add widgets, bridges & bots": "Aldonu fenestraĵojn, pontojn, kaj robotojn", + "Edit widgets, bridges & bots": "Redakti fenestraĵojn, pontojn, kaj robotojn", + "Widgets": "Fenestraĵoj", + "Unpin a widget to view it in this panel": "Malfiksu fenestraĵon por vidi ĝin sur ĉi tiu breto", + "Unpin": "Malfiksi", + "You can only pin up to %(count)s widgets|other": "Vi povas fiksi maksimume %(count)s fenestraĵojn", + "Room Info": "Informoj pri ĉambro", + "%(count)s results|one": "%(count)s rezulto", + "%(count)s results|other": "%(count)s rezultoj", + "Explore all public rooms": "Esplori ĉiujn publiajn ĉambrojn", + "Can't see what you’re looking for?": "Ĉu vi ne sukcesas trovi la serĉaton?", + "Explore public rooms": "Esplori publikajn ĉambrojn", + "Explore community rooms": "Esplori komunumajn ĉambrojn", + "Show Widgets": "Montri fenestraĵojn", + "Hide Widgets": "Kaŝi fenestraĵojn", + "Remove messages sent by others": "Forigi mesaĝojn senditajn de aliaj", + "Privacy": "Privateco", + "Secure Backup": "Sekura savkopiado", + "not ready": "neprete", + "ready": "prete", + "Secret storage:": "Sekreta deponejo:", + "Backup key cached:": "Kaŝmemorita Savkopia ŝlosilo:", + "Backup key stored:": "Deponita Savkopia ŝlosilo:", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Savkopiu viajn ĉifrajn ŝlosilojn kun la datumoj de via konto, pro eblo ke vi perdus aliron al viaj salutaĵoj. Viaj ŝlosiloj estos sekurigitaj per unika Rehava ŝlosilo.", + "Algorithm:": "Algoritmo:", + "Backup version:": "Repaŝa versio:", + "The operation could not be completed": "La ago ne povis finiĝi", + "Failed to save your profile": "Malsukcesis konservi vian profilon", + "Master private key:": "Ĉefa privata ŝlosilo:", + "not found in storage": "netrovite en deponejo", + "Cross-signing is not set up.": "Delegaj subskriboj ne estas agorditaj.", + "Cross-signing is ready for use.": "Delegaj subskriboj estas pretaj por uzado.", + "Downloading logs": "Elŝutante protokolon", + "Uploading logs": "Alŝutante protokolon", + "Safeguard against losing access to encrypted messages & data": "Malhelpu perdon de aliro al ĉifritaj mesaĝoj kaj datumoj", + "Set up Secure Backup": "Agordi Sekuran savkopiadon", + "Unknown App": "Nekonata aplikaĵo", + "Error leaving room": "Eraro dum foriro de la ĉambro", + "Unexpected server error trying to leave the room": "Neatendita servila eraro dum foriro de ĉambro", + "%(senderName)s declined the call.": "%(senderName)s rifuzis la vokon.", + "(an error occurred)": "(okazis eraro)", + "(their device couldn't start the camera / microphone)": "(ĝia aparato ne povis funkciigi la filmilon / mikrofonon)", + "(connection failed)": "(konekto malsukcesis)", + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Ĉiuj serviloj estas forbaritaj de partoprenado! La ĉambro ne plu povas esti uzata.", + "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Antaŭmetas ( ͡° ͜ʖ ͡°) al platteksta mesaĝo", + "This will end the conference for everyone. Continue?": "Ĉi tio finos la grupan vokon por ĉiuj. Ĉu daŭrigi?", + "End conference": "Fini grupan vokon", + "The call was answered on another device.": "La voko estis respondita per alia aparato.", + "Answered Elsewhere": "Respondita aliloke", + "The call could not be established": "Ne povis meti la vokon.", + "The other party declined the call.": "La alia persono rifuzis la vokon.", + "Call Declined": "Voko rifuziĝis" } From 94c814e527755389ea1b8f8970cfc5c1832f74fb Mon Sep 17 00:00:00 2001 From: random Date: Fri, 23 Oct 2020 09:51:47 +0000 Subject: [PATCH 0098/4306] Translated using Weblate (Italian) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index abb8ccbedc..a70cfe7d3e 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2541,7 +2541,7 @@ "The call could not be established": "Impossibile stabilire la chiamata", "The other party declined the call.": "Il destinatario ha rifiutato la chiamata.", "Call Declined": "Chiamata rifiutata", - "Offline encrypted messaging using dehydrated devices": "Messaggistica offline criptata usando dispositivi \"deidratati\"", + "Offline encrypted messaging using dehydrated devices": "Messaggistica offline criptata usando dispositivi \"disidratati\"", "Move right": "Sposta a destra", "Move left": "Sposta a sinistra", "Revoke permissions": "Revoca autorizzazioni", From e85a60495e3ba8a40162a740de5e86daf88b4814 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 23 Oct 2020 05:02:19 +0000 Subject: [PATCH 0099/4306] Translated using Weblate (Finnish) Currently translated at 89.5% (2119 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index d7aa91346e..6c20ed2fae 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -2244,5 +2244,24 @@ "You can only pin 2 widgets at a time": "Vain kaksi sovelmaa voi olla kiinnitettynä samaan aikaan", "Add widgets, bridges & bots": "Lisää sovelmia, siltoja ja botteja", "Edit widgets, bridges & bots": "Muokkaa sovelmia, siltoja ja botteja", - "Widgets": "Sovelmat" + "Widgets": "Sovelmat", + "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Yhteisöjen v2 prototyypit. Vaatii yhteensopivan kotipalvelimen. Erittäin kokeellinen — käytä varoen.", + "Change notification settings": "Muokkaa ilmoitusasetuksia", + "The person who invited you already left the room, or their server is offline.": "Henkilö, joka kutsui sinut, on jo lähtenyt huoneesta, tai hänen palvelin on pois verkosta.", + "The person who invited you already left the room.": "Henkilö, joka kutsui sinut, lähti jo huoneesta.", + "Unknown App": "Tuntematon sovellus", + "Error leaving room": "Virhe poistuessa huoneesta", + "Unexpected server error trying to leave the room": "Huoneesta poistuessa tapahtui odottamaton palvelinvirhe", + "%(senderName)s declined the call.": "%(senderName)s hylkäsi puhelun.", + "(an error occurred)": "(virhe tapahtui)", + "(their device couldn't start the camera / microphone)": "(hänen laitteensa ei voinut käynnistää kameraa tai mikrofonia)", + "(connection failed)": "(yhteys katkesi)", + "🎉 All servers are banned from participating! This room can no longer be used.": "Kaikki palvelimet ovat saaneet porttikiellon huoneeseen! Tätä huonetta ei voi enää käyttää.", + "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Lisää hymiön ( ͡° ͜ʖ ͡°) viestin alkuun", + "Are you sure you want to cancel entering passphrase?": "Haluatko varmasti peruuttaa salalauseen syöttämisen?", + "The call was answered on another device.": "Puheluun vastattiin toisessa laitteessa.", + "Answered Elsewhere": "Vastattu muualla", + "The call could not be established": "Puhelua ei voitu aloittaa", + "The other party declined the call.": "Toinen osapuoli hylkäsi puhelun.", + "Call Declined": "Puhelu hylätty" } From e55d463c247915def483e1730c32504dd50562b0 Mon Sep 17 00:00:00 2001 From: XoseM Date: Fri, 23 Oct 2020 05:49:59 +0000 Subject: [PATCH 0100/4306] Translated using Weblate (Galician) Currently translated at 99.9% (2364 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index e32874dd82..57fc1f645d 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -2537,5 +2537,14 @@ "(connection failed)": "(fallou a conexión)", "The call could not be established": "Non se puido establecer a chamada", "The other party declined the call.": "A outra persoa rexeitou a chamada.", - "Call Declined": "Chamada rexeitada" + "Call Declined": "Chamada rexeitada", + "Move right": "Mover á dereita", + "Move left": "Mover á esquerda", + "Revoke permissions": "Revogar permisos", + "Unpin a widget to view it in this panel": "Desafixar un widget para velo neste panel", + "You can only pin up to %(count)s widgets|other": "Só podes fixar ata %(count)s widgets", + "Show Widgets": "Mostrar Widgets", + "Hide Widgets": "Agochar Widgets", + "The call was answered on another device.": "A chamada foi respondida noutro dispositivo.", + "Answered Elsewhere": "Respondido noutro lugar" } From 82f1d9850e7323aa52621d89eeb8bab8040ad47a Mon Sep 17 00:00:00 2001 From: MamasLT Date: Fri, 23 Oct 2020 13:29:45 +0000 Subject: [PATCH 0101/4306] Translated using Weblate (Lithuanian) Currently translated at 71.5% (1692 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 245 +++++++++++++++++++++++++++++++-------- 1 file changed, 197 insertions(+), 48 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 9bdbb7a6f7..c1d450bdef 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -8,7 +8,7 @@ "Which officially provided instance you are using, if any": "Kurią oficialiai teikiamą instanciją naudojate, jei tokių yra", "Whether or not you're using the Richtext mode of the Rich Text Editor": "Ar jūs naudojate Raiškiojo Teksto Redaktoriaus Raiškiojo Teksto režimą ar ne", "Your homeserver's URL": "Jūsų serverio URL", - "Analytics": "Statistika", + "Analytics": "Analitika", "The information being sent to us to help make %(brand)s better includes:": "Informacija, siunčiama mums siekiant pagerinti %(brand)s, yra:", "Fetching third party location failed": "Nepavyko gauti trečios šalies vietos", "I understand the risks and wish to continue": "Suprantu šią riziką ir noriu tęsti", @@ -79,7 +79,7 @@ "%(brand)s uses many advanced browser features, some of which are not available or experimental in your current browser.": "%(brand)s naudoja daug išplėstinių naršyklės funkcijų, kai kurios iš jų yra neprieinamos arba eksperimentinės jūsų esamoje naršyklėje.", "Event sent!": "Įvykis išsiųstas!", "Unnamed room": "Kambarys be pavadinimo", - "Dismiss": "Atmesti", + "Dismiss": "Atsisakyti", "Explore Account Data": "Peržiūrėti paskyros duomenis", "Remove from Directory": "Pašalinti iš katalogo", "Download this file": "Atsisiųsti šį failą", @@ -124,7 +124,7 @@ "Forward Message": "Persiųsti žinutę", "Back": "Atgal", "Reply": "Atsakyti", - "Show message in desktop notification": "Rodyti žinutes darbalaukio pranešimuose", + "Show message in desktop notification": "Rodyti žinutę darbalaukio pranešime", "Reject": "Atmesti", "Sorry, your browser is not able to run %(brand)s.": "Atleiskite, jūsų naršyklė negali paleisti %(brand)s.", "Quote": "Cituoti", @@ -160,13 +160,13 @@ "Checking for an update...": "Tikrinama ar yra atnaujinimų...", "e.g. %(exampleValue)s": "pvz., %(exampleValue)s", "e.g. ": "pvz., ", - "Your device resolution": "Jūsų įrenginio raiška", - "Call Failed": "Skambutis nepavyko", + "Your device resolution": "Jūsų įrenginio skyra", + "Call Failed": "Skambutis Nepavyko", "Unable to capture screen": "Nepavyko nufotografuoti ekrano", - "You are already in a call.": "Jūs jau dalyvaujate skambutyje.", + "You are already in a call.": "Jūs jau esate skambutyje.", "VoIP is unsupported": "VoIP yra nepalaikoma", - "Permission Required": "Reikalingas leidimas", - "Upload Failed": "Įkėlimas nepavyko", + "Permission Required": "Reikalingas Leidimas", + "Upload Failed": "Įkėlimas Nepavyko", "Sun": "Sek", "Mon": "Pir", "Tue": "Ant", @@ -192,27 +192,27 @@ "%(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, %(fullYear)s %(monthName)s %(day)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(fullYear)s %(monthName)s %(day)s %(time)s", - "Who would you like to add to this community?": "Ką norėtumėte pridėti į šią bendruomenę?", + "Who would you like to add to this community?": "Ką norėtumėte pridėti prie šios bendruomenės?", "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Įspėjimas: bet kuris jūsų pridėtas asmuo bus viešai matomas visiems, žinantiems bendruomenės ID", - "Invite to Community": "Pakviesti į bendruomenę", - "Which rooms would you like to add to this community?": "Kuriuos kambarius norėtumėte pridėti į šią bendruomenę?", - "Add rooms to the community": "Pridėti kambarius į bendruomenę", - "Add to community": "Pridėti į bendruomenę", + "Invite to Community": "Pakviesti į Bendruomenę", + "Which rooms would you like to add to this community?": "Kuriuos kambarius norėtumėte pridėti prie šios bendruomenės?", + "Add rooms to the community": "Pridėti kambarius prie bendruomenės", + "Add to community": "Pridėti prie bendruomenės", "Failed to invite the following users to %(groupId)s:": "Nepavyko pakviesti šių vartotojų į %(groupId)s:", "Failed to invite users to community": "Nepavyko pakviesti vartotojų į bendruomenę", "Failed to invite users to %(groupId)s": "Nepavyko pakviesti vartotojų į %(groupId)s", - "Failed to add the following rooms to %(groupId)s:": "Nepavyko pridėti šių kambarių į %(groupId)s:", + "Failed to add the following rooms to %(groupId)s:": "Nepavyko pridėti šių kambarių prie %(groupId)s:", "%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s neturi leidimo siųsti jums pranešimus - patikrinkite savo naršyklės nustatymus", "%(brand)s was not given permission to send notifications - please try again": "%(brand)s nebuvo suteiktas leidimas siųsti pranešimus - bandykite dar kartą", - "Unable to enable Notifications": "Nepavyko įjungti pranešimų", - "This email address was not found": "Šis el. pašto adresas nebuvo rastas", + "Unable to enable Notifications": "Nepavyko įjungti Pranešimų", + "This email address was not found": "Šis el. pašto adresas buvo nerastas", "Admin": "Administratorius", "Failed to invite": "Nepavyko pakviesti", "Failed to invite the following users to the %(roomName)s room:": "Nepavyko pakviesti šių vartotojų į kambarį %(roomName)s:", - "You need to be logged in.": "Turite būti prisijungę.", + "You need to be logged in.": "Jūs turite būti prisijungę.", "Unable to create widget.": "Nepavyko sukurti valdiklio.", "Failed to send request.": "Nepavyko išsiųsti užklausos.", - "This room is not recognised.": "Šis kambarys neatpažintas.", + "This room is not recognised.": "Šis kambarys yra neatpažintas.", "You are not in this room.": "Jūs nesate šiame kambaryje.", "You do not have permission to do that in this room.": "Jūs neturite leidimo tai atlikti šiame kambaryje.", "Room %(roomId)s not visible": "Kambarys %(roomId)s nematomas", @@ -240,7 +240,7 @@ "%(senderName)s answered the call.": "%(senderName)s atsiliepė į skambutį.", "(unknown failure: %(reason)s)": "(nežinoma klaida: %(reason)s)", "%(senderName)s ended the call.": "%(senderName)s užbaigė skambutį.", - "Unnamed Room": "Bevardis kambarys", + "Unnamed Room": "Bevardis Kambarys", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Rodyti laiko žymes 12 valandų formatu (pvz. 2:30pm)", "Always show message timestamps": "Visada rodyti žinučių laiko žymes", "Always show encryption icons": "Visada rodyti šifravimo piktogramas", @@ -417,7 +417,7 @@ "%(widgetName)s widget modified by %(senderName)s": "%(senderName)s modifikavo %(widgetName)s valdiklį", "%(widgetName)s widget added by %(senderName)s": "%(senderName)s pridėjo %(widgetName)s valdiklį", "%(widgetName)s widget removed by %(senderName)s": "%(senderName)s pašalino %(widgetName)s valdiklį", - "Failure to create room": "Nepavyko sukurti kambarį", + "Failure to create room": "Nepavyko sukurti kambario", "Server may be unavailable, overloaded, or you hit a bug.": "Serveris gali būti neprieinamas, per daug apkrautas, arba susidūrėte su klaida.", "Autoplay GIFs and videos": "Automatiškai atkurti GIF ir vaizdo įrašus", "This event could not be displayed": "Nepavyko parodyti šio įvykio", @@ -471,7 +471,7 @@ "Share room": "Bendrinti kambarį", "Usage": "Naudojimas", "Searches DuckDuckGo for results": "Atlieka rezultatų paiešką sistemoje DuckDuckGo", - "To use it, just wait for autocomplete results to load and tab through them.": "Norėdami tai naudoti, tiesiog palaukite, kol bus įkelti automatiškai užbaigti rezultatai, tuomet pereikite per juos naudodami Tab klavišą.", + "To use it, just wait for autocomplete results to load and tab through them.": "Norėdami tai naudoti, tiesiog palaukite, kol bus įkelti automatiškai užbaigti rezultatai, tuomet pereikite per juos naudodami tab klavišą.", "%(targetName)s left the room.": "%(targetName)s išėjo iš kambario.", "%(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.", @@ -486,14 +486,14 @@ "The conversation continues here.": "Pokalbis tęsiasi čia.", "Jump to message": "Pereiti prie žinutės", "Favourites": "Mėgstami", - "Banned users": "Užblokuoti naudotojai", + "Banned users": "Užblokuoti vartotojai", "This room is not accessible by remote Matrix servers": "Šis kambarys nėra pasiekiamas nuotoliniams Matrix serveriams", "Who can read history?": "Kas gali skaityti istoriją?", "Only room administrators will see this warning": "Šį įspėjimą matys tik kambario administratoriai", - "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.", + "You have enabled URL previews by default.": "Jūs įjungėte URL nuorodų peržiūras kaip numatytasias.", + "You have disabled URL previews by default.": "Jūs išjungėte URL nuorodų peržiūras kaip numatytasias.", + "URL previews are enabled by default for participants in this room.": "URL nuorodų peržiūros šio kambario dalyviams yra įjungtos kaip numatytosios.", + "URL previews are disabled by default for participants in this room.": "URL nuorodų peržiūros šio kambario dalyviams yra išjungtos kaip numatytosios.", "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.", @@ -532,8 +532,8 @@ "Incorrect password": "Neteisingas slaptažodis", "To continue, please enter your password:": "Norėdami tęsti, įveskite savo slaptažodį:", "An error has occurred.": "Įvyko klaida.", - "Failed to upgrade room": "Nepavyko atnaujinti kambarį", - "The room upgrade could not be completed": "Nepavyko užbaigti kambario naujinimo", + "Failed to upgrade room": "Nepavyko atnaujinti kambario", + "The room upgrade could not be completed": "Nepavyko užbaigti kambario atnaujinimo", "Sign out": "Atsijungti", "Send Logs": "Siųsti žurnalus", "Refresh": "Įkelti iš naujo", @@ -578,7 +578,7 @@ "%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)s pasikeitė pseudoportretus %(count)s kartų(-us)", "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s pasikeitė pseudoportretą %(count)s kartų(-us)", "And %(count)s more...|other": "Ir dar %(count)s...", - "Existing Call": "Esamas skambutis", + "Existing Call": "Esamas Skambutis", "A call is already in progress!": "Skambutis jau vyksta!", "Default": "Numatytas", "Restricted": "Apribotas", @@ -676,8 +676,8 @@ "This server does not support authentication with a phone number.": "Šis serveris nepalaiko tapatybės nustatymo telefono numeriu.", "Download": "Atsisiųsti", "Retry": "Bandyti dar kartą", - "Add Email Address": "Pridėti el. pašto adresą", - "Add Phone Number": "Pridėti telefono numerį", + "Add Email Address": "Pridėti El. Pašto Adresą", + "Add Phone Number": "Pridėti Telefono Numerį", "Whether or not you're logged in (we don't record your username)": "Ar jūs prisijungę ar ne (mes neįrašome jūsų vartotojo vardo)", "Chat with %(brand)s Bot": "Kalbėtis su %(brand)s Botu", "Sign In": "Prisijungti", @@ -687,15 +687,15 @@ "Sign in to your Matrix account on ": "Prisijunkite prie savo paskyros serveryje", "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Ar jūs naudojate 'duonos trupinių' funkciją ar ne (pseudoportretai virš kambarių sąrašo)", "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ten, kur šis puslapis įtraukia identifikuojamą informaciją, kaip kambarys, vartotojas ar grupės ID, tie duomenys yra pašalinami prieš siunčiant į serverį.", - "The remote side failed to pick up": "Nuotolinėi pusėi nepavyko atsiliepti", + "The remote side failed to pick up": "Nuotolinei pusei nepavyko atsiliepti", "Call failed due to misconfigured server": "Skambutis nepavyko dėl neteisingai sukonfigūruoto serverio", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Paprašykite savo serverio administratoriaus (%(homeserverDomain)s) sukonfiguruoti TURN serverį, kad skambučiai veiktų patikimai.", "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.": "Alternatyviai, jūs galite bandyti naudoti viešą serverį turn.matrix.org, bet tai nebus taip patikima, ir tai atskleis jūsų IP adresą šiam serveriui. Jūs taip pat galite tvarkyti tai Nustatymuose.", - "Try using turn.matrix.org": "Bandykite naudoti turn.matrix.org", - "Call in Progress": "Vykstantis skambutis", + "Try using turn.matrix.org": "Bandyti naudojant turn.matrix.org", + "Call in Progress": "Vykstantis Skambutis", "A call is currently being placed!": "Šiuo metu skambinama!", - "Replying With Files": "Atsakyti su failais", - "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Šiuo metu neįmanoma atsakyti su failu. Ar norite įkelti šį failą neatsakydami?", + "Replying With Files": "Atsakyti Su Failais", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Šiuo metu neįmanoma atsakyti su failu. Ar norėtumėte įkelti šį failą neatsakydami?", "The file '%(fileName)s' failed to upload.": "Failo '%(fileName)s' nepavyko įkelti.", "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Failas '%(fileName)s' viršyja šio serverio įkeliamų failų dydžio limitą", "The server does not support the room version specified.": "Serveris nepalaiko nurodytos kambario versijos.", @@ -703,7 +703,7 @@ "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 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.", + "Only continue if you trust the owner of the server.": "Tęskite tik tuo atveju, jei pasitikite serverio savininku.", "Trust": "Pasitikėti", "Failed to invite users to the room:": "Nepavyko pakviesti vartotojų į kambarį:", "You need to be able to invite users to do that.": "Norėdami tai atlikti jūs turite turėti galimybę pakviesti vartotojus.", @@ -711,7 +711,7 @@ "Messages": "Žinutės", "Actions": "Veiksmai", "Other": "Kitas", - "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prideda ¯\\_(ツ)_/¯ prie paprasto teksto pranešimo", + "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prideda ¯\\_(ツ)_/¯ prie paprasto teksto žinutės", "Sends a message as plain text, without interpreting it as markdown": "SIunčia žinutę, kaip paprastą tekstą, jo neinterpretuodamas kaip pažymėto", "Upgrades a room to a new version": "Atnaujina kambarį į naują versiją", "You do not have the required permissions to use this command.": "Jūs neturite reikalingų leidimų naudoti šią komandą.", @@ -724,7 +724,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 toliau 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.", "Unbans user with given ID": "Atblokuoja vartotoją su nurodytu id", "Ignored user": "Ignoruojamas vartotojas", @@ -822,7 +822,7 @@ "Explore": "Žvalgyti", "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 vėl prie jo prisijungti be pakvietimo.", + "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 prisijungti iš naujo be pakvietimo.", "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į.", "%(brand)s failed to get the public room list.": "%(brand)s nepavyko gauti viešų kambarių sąrašo.", @@ -1157,11 +1157,11 @@ "This requires the latest %(brand)s on your other devices:": "Tam reikia naujausios %(brand)s versijos kituose jūsų įrenginiuose:", "or another cross-signing capable Matrix client": "arba kito kryžminį pasirašymą palaikančio Matrix kliento", "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", + "Use Single Sign On to continue": "Norėdami tęsti naudokite Vieną Prisijungimą", + "Confirm adding this email address by using Single Sign On to prove your identity.": "Patvirtinkite šio el. pašto adreso pridėjimą naudodami Vieną Prisijungimą, kad įrodytumėte savo tapatybę.", + "Single Sign On": "Vienas Prisijungimas", "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 this phone number by using Single Sign On to prove your identity.": "Patvirtinkite šio tel. nr. pridėjimą naudodami Vieną Prisijungimą, kad įrodytumėte savo 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ą.", "Match system theme": "Suderinti su sistemos tema", @@ -1210,7 +1210,7 @@ "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.": "Dar kartą įsitikinkite, kad jūsų serveris palaiko pasirinktą kambario versiją ir bandykite iš naujo.", - "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Ar naudojate %(brand)s įrenginyje, kuriame pagrindinis įvesties mechanizmas yra lietimas", + "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Ar jūs naudojate %(brand)s į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 Emoji suggestions while typing": "Įjungti jaustukų pasiūlymus rašant", @@ -1426,7 +1426,7 @@ "Reject & Ignore user": "Atmesti ir ignoruoti vartotoją", "Reject invitation": "Atmesti pakvietimą", "Unable to reject invite": "Nepavyko atmesti pakvietimo", - "Whether you're using %(brand)s as an installed Progressive Web App": "Ar naudojate %(brand)s kaip įdiegtą progresyviąją žiniatinklio programą", + "Whether you're using %(brand)s as an installed Progressive Web App": "Ar jūs naudojate %(brand)s kaip Įdiegtą Progresyviąją Žiniatinklio Programą", "Your user agent": "Jūsų vartotojo agentas", "Invite only": "Tik pakviestiems", "You can only join it with a working invite.": "Jūs galite prisijungti tik su veikiančiu pakvietimu.", @@ -1436,7 +1436,7 @@ "%(name)s is requesting verification": "%(name)s prašo patvirtinimo", "Sign In or Create Account": "Prisijungti arba Sukurti Paskyrą", "Use your account or create a new one to continue.": "Norėdami tęsti naudokite savo paskyrą arba sukurkite naują.", - "Create Account": "Sukurti paskyrą", + "Create Account": "Sukurti Paskyrą", "Custom (%(level)s)": "Pasirinktinis (%(level)s)", "Ask this user to verify their session, or manually verify it below.": "Paprašykite šio vartotojo patvirtinti savo seansą arba patvirtinkite jį rankiniu būdu žemiau.", "Encryption upgrade available": "Galimas šifravimo atnaujinimas", @@ -1627,5 +1627,154 @@ "Move autocomplete selection up/down": "Perkelti automatinio užbaigimo pasirinkimą aukštyn/žemyn", "Cancel autocomplete": "Atšaukti automatinį užbaigimą", "Please install Chrome, Firefox, or Safari for the best experience.": "Geriausiam veikimui suinstaliuokite Chrome, Firefox, arba Safari.", - "Use Recovery Key or Passphrase": "Naudoti atgavimo raktą arba slaptafrazę" + "Use Recovery Key or Passphrase": "Naudoti atgavimo raktą arba slaptafrazę", + "Error upgrading room": "Klaida atnaujinant kambarį", + "Sends a message as html, without interpreting it as markdown": "SIunčia žinutę, kaip html, jo neinterpretuodamas kaip pažymėto", + "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prideda ( ͡° ͜ʖ ͡°) prie paprasto teksto žinutės", + "Are you sure you want to cancel entering passphrase?": "Ar tikrai norite atšaukti slaptafrazės įvedimą?", + "Offline encrypted messaging using dehydrated devices": "Šifruoti pranešimai neprisijungus naudojant dehidruotus įrenginius", + "Clear notifications": "Išvalyti pranešimus", + "Feedback": "Atsiliepimai", + "All settings": "Visi nustatymai", + "Security & privacy": "Saugumas ir privatumas", + "Notification settings": "Pranešimų nustatymai", + "Change notification settings": "Keisti pranešimų nustatymus", + "Upgrade to your own domain": "Perkelti į savo domeną", + "Room Info": "Kambario info", + "Open Devtools": "Atverti Įrankius", + "Developer options": "Programuotojo parinktys", + "View older messages in %(roomName)s.": "Peržiūrėti senesnes žinutes %(roomName)s.", + "Room version:": "Kambario versija:", + "Room version": "Kambario versija", + "Upgrade this room to the recommended room version": "Atnaujinti šį kambarį į rekomenduojamą kambario versiją", + "Internal room ID:": "Vidinis kambario ID:", + "Room information": "Kambario informacija", + "Browse": "Naršyti", + "Set a new custom sound": "Nustatyti naują pasirinktinį garsą", + "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.": "Paskyros deaktyvavimas, pagal nutylėjimą, nepriverčia mūsų sistemos užmiršti jūsų siųstų žinučių. Jei norite, kad jūsų žinutės mūsų sistemos būtų užmirštos, pažymėkite žemiau esantį laukelį.", + "Use default": "Naudoti numatytąjį", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Serverio administratorius išjungė visapusį šifravimą kaip numatytą privačiuose kambariuose ir Tiesioginėse Žinutėse.", + "Notification sound": "Pranešimo garsas", + "Sounds": "Garsai", + "Privileged Users": "Privilegijuoti Nariai", + "Roles & Permissions": "Rolės ir Leidimai", + "Members only (since the point in time of selecting this option)": "Tik nariai (nuo šios parinkties pasirinkimo momento)", + "Members only (since they were invited)": "Tik nariai (nuo jų pakvietimo)", + "Only visible to community members": "Matoma tik bendruomenės nariams", + "Members only (since they joined)": "Tik nariai (nuo jų prisijungimo)", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Kas gali skaityti istoriją nustatymų pakeitimai bus taikomi tik būsimoms šio kambario žinutėms. Esamos istorijos matomumas nepakis.", + "The authenticity of this encrypted message can't be guaranteed on this device.": "Šiame įrenginyje negalima užtikrinti šios užšifruotos žinutės autentiškumo.", + "Unencrypted": "Neužšifruota", + "Encrypted by an unverified session": "Užšifruota nepatvirtinto seanso", + "Encrypted": "Užšifruota", + "Securely cache encrypted messages locally for them to appear in search results.": "Šifruotus pranešimus saugiai talpinkite lokalioje talpykloje, kad jie būtų rodomi paieškos rezultatuose.", + "Securely cache encrypted messages locally for them to appear in search results, using ": "Šifruotus pranešimus saugiai talpinkite lokalioje talpykloje, kad jie būtų rodomi paieškos rezultatuose, naudojant ", + "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Saugios žinutės su šiuo vartotoju yra visapusiškai užšifruotos ir jų negali perskaityti trečiosios šalys.", + "Safeguard against losing access to encrypted messages & data": "Apsisaugokite nuo prieigos prie šifruotų žinučių ir duomenų praradimo", + "Main address": "Pagrindinis adresas", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Atnaujinant pagrindinį kambario adresą įvyko klaida. Gali būti, kad serveris to neleidžia arba įvyko laikina klaida.", + "Error updating main address": "Atnaujinant pagrindinį adresą įvyko klaida", + "Published Addresses": "Paskelbti Adresai", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Atnaujinant kambario alternatyvius adresus įvyko klaida. Gali būti, kad serveris to neleidžia arba įvyko laikina klaida.", + "Room Addresses": "Kambario Adresai", + "%(senderName)s changed the addresses for this room.": "%(senderName)s pakeitė šio kambario adresus.", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s pakeitė pagrindinį ir alternatyvius šio kambario adresus.", + "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s pakeitė alternatyvius šio kambario adresus.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s pridėjo alternatyvų šio kambario adresą %(addresses)s.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s pridėjo alternatyvius šio kambario adresus %(addresses)s.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s pašalino alternatyvų šio kambario adresą %(addresses)s.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s pašalino alternatyvius šio kambario adresus %(addresses)s.", + "Room settings": "Kambario nustatymai", + "Link to most recent message": "Nuoroda į naujausią žinutę", + "Invite someone using their name, username (like ) or share this room.": "Pakviesti ką nors naudojant jų vardą, vartotojo vardą (pavyzdžiui ) arba bendrinti šį kambarį.", + "Share Room Message": "Bendrinti Kambario Žinutę", + "Share Room": "Bendrinti Kambarį", + "Use bots, bridges, widgets and sticker packs": "Naudoti botus, tiltus, valdiklius ir lipdukų pakuotes", + "Add widgets, bridges & bots": "Pridėti valdiklius, tiltus ir botus", + "Edit widgets, bridges & bots": "Redaguoti valdiklius, tiltus ir botus", + "Widgets": "Valdikliai", + "You can only pin up to %(count)s widgets|other": "Galite prisegti tik iki %(count)s valdiklių", + "Hide Widgets": "Slėpti Valdiklius", + "Modify widgets": "Keisti valdiklius", + "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "%(brand)s dabar naudoja 3-5 kartus mažiau atminties, įkeliant vartotojų informaciją tik prireikus. Palaukite, kol mes iš naujo sinchronizuosime su serveriu!", + "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "Jūs ketinate pašalinti %(count)s %(user)s žinutes(-ų). To negalima anuliuoti. Ar norite tęsti?", + "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|one": "Jūs ketinate pašalinti 1 %(user)s žinutę. To negalima anuliuoti. Ar norite tęsti?", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Jūs būsite nukreipti į trečiosios šalies svetainę, kad galėtumėte patvirtinti savo paskyrą naudojimui su %(integrationsUrl)s. Ar norite tęsti?", + "About": "Apie", + "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.": "Įjungus kambario šifravimą jo išjungti negalima. Žinutės, siunčiamos šifruotame kambaryje, nėra matomos serverio. Jas gali matyti tik kambario dalyviai. Įjungus šifravimą, daugelis botų ir tiltų gali veikti netinkamai. Sužinoti daugiau apie šifravimą.", + "about a day from now": "apie dieną nuo dabar", + "about an hour from now": "apie valandą nuo dabar", + "about a minute from now": "apie minutę nuo dabar", + "about a day ago": "maždaug prieš dieną", + "about an hour ago": "maždaug prieš valandą", + "about a minute ago": "maždaug prieš minutę", + "Displays information about a user": "Rodo informaciją apie vartotoją", + "Error whilst fetching joined communities": "Gaunant prisijungtas bendruomenes įvyko klaida", + "Who can join this community?": "Kas gali prisijungti prie šios bendruomenės?", + "Join this community": "Prisijungti prie šios bendruomenės", + "%(inviter)s has invited you to join this community": "%(inviter)s pakvietė jus prisijungti prie šios bendruomenės", + "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Šie kambariai rodomi bendruomenės nariams bendruomenės puslapyje. Bendruomenės nariai gali prisijungti prie kambarių paspausdami ant jų.", + "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Jūs esate šios bendruomenės administratorius. Jūs negalėsite prisijungti iš naujo be kito administratoriaus kvietimo.", + "Unable to join community": "Nepavyko prisijungti prie bendruomenės", + "You must join the room to see its files": "Norėdami pamatyti jo failus, turite prisijungti prie kambario", + "Join millions for free on the largest public server": "Prisijunkite prie milijonų didžiausiame viešame serveryje nemokamai", + "Block anyone not part of %(serverName)s from ever joining this room.": "Blokuoti bet ką, kas nėra iš %(serverName)s, niekada nebeleidžiant prisijungti prie šio kambario.", + "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Privačius kambarius rasti ir prie jų prisijungti galima tik su pakvietimu. Viešus kambarius rasti ir prie jų prisijungti gali visi šioje bendruomenėje.", + "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone.": "Privačius kambarius rasti ir prie jų prisijungti galima tik su pakvietimu. Viešus kambarius rasti ir prie jų prisijungti gali visi.", + "Join": "Prisijungti", + "Join the conference from the room information card on the right": "Prisijunkite prie konferencijos iš kambario informacijos kortelės dešinėje", + "Join the conference at the top of this room": "Prisijunkite prie konferencijos šio kambario viršuje", + "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.": "Paskelbtus adresus gali naudoti bet kas, bet kuriame serveryje, kad prisijungtų prie jūsų kambario. Tam, kad paskelbtumėte adresą, visų pirma jis turi būti nustatytas kaip lokalus adresas.", + "Join the discussion": "Prisijungti prie diskusijos", + "Joining room …": "Jungiamasi prie kambario …", + "Try to join anyway": "Vis tiek bandyti prisijungti", + "Re-join": "Prisijungti iš naujo", + "Join the conversation with an account": "Prisijunkite prie pokalbio su paskyra", + "Join Room": "Prisijungti prie kambario", + "Guests cannot join this room even if explicitly invited.": "Svečiai negali prisijungti prie šio kambario, net jei jie yra pakviesti.", + "Subscribing to a ban list will cause you to join it!": "Užsiprenumeravus draudimų sąrašą, būsite prie jo prijungtas!", + "%(senderName)s joined the call": "%(senderName)s prisijungė prie skambučio", + "You joined the call": "Jūs prisijungėte prie skambučio", + "Joins room with given address": "Prisijungia prie kambario su nurodytu adresu", + "Your community hasn't got a Long Description, a HTML page to show to community members.
Click here to open settings and give it one!": "Jūsų bendruomenė neturi ilgo aprašymo, HTML puslapio, kuris galėtų būti rodomas bendruomenės nariams.
Spauskite čia, norėdami atidaryti nustatymus ir tai pridėti!", + "Show": "Rodyti", + "Show all": "Rodyti viską", + "You have ignored this user, so their message is hidden. Show anyways.": "Jūs ignoravote šį vartotoją, todėl jo žinutė yra paslėpta. Rodyti vistiek.", + "Show files": "Rodyti failus", + "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Kai kas nors į savo žinutę įtraukia URL, gali būti rodoma URL peržiūra, suteikianti daugiau informacijos apie tą nuorodą, tokios kaip pavadinimas, aprašymas ir vaizdas iš svetainės.", + "Show Stickers": "Rodyti Lipdukus", + "Show %(count)s more|one": "Rodyti dar %(count)s", + "Show %(count)s more|other": "Rodyti dar %(count)s", + "Show previews of messages": "Rodyti žinučių peržiūras", + "Show rooms with unread messages first": "Pirmiausia rodyti kambarius su neperskaitytomis žinutėmis", + "Show Widgets": "Rodyti Valdiklius", + "Show tray icon and minimize window to it on close": "Rodyti dėklo piktogramą ir sumažinti langą į jį, kai uždaroma", + "Always show the window menu bar": "Visada rodyti lango meniu juostą", + "There are advanced notifications which are not shown here.": "Yra išplėstinių pranešimų, kurie čia nerodomi.", + "Show less": "Rodyti mažiau", + "Show message previews for reactions in all rooms": "Rodyti žinučių peržiūras reakcijoms visuose kambariuose", + "Show message previews for reactions in DMs": "Rodyti žinučių peržiūras reakcijoms tiesioginėse žinutėse", + "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Neleisti vartotojams kalbėti senoje kambario versijoje ir paskelbti pranešimą, kuriame vartotojams patariama persikelti į naują kambarį", + "Update any local room aliases to point to the new room": "Atnaujinkite vietinių kambarių slapyvardžius, kad nurodytumėte į naująjį kambarį", + "Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:": "Norint atnaujinti šį kambarį, reikia uždaryti esamą kambario instanciją ir vietoje jo sukurti naują kambarį. Norėdami suteikti kambario nariams kuo geresnę patirtį, mes:", + "Upgrade Room Version": "Atnaujinti Kambario Versiją", + "Upgrade this room to version %(version)s": "Atnaujinti šį kambarį į %(version)s versiją", + "Please fill why you're reporting.": "Įrašykite kodėl pranešate.", + "Put a link back to the old room at the start of the new room so people can see old messages": "Naujojo kambario pradžioje įdėkite nuorodą į senąjį kambarį, kad žmonės galėtų matyti senas žinutes", + "An image will help people identify your community.": "Atvaizdas padės žmonėms atpažinti jūsų bendruomenę.", + "Invite people to join %(communityName)s": "Pakviesti žmones jungtis prie %(communityName)s", + "People you know on %(brand)s": "Žmonės, kuriuos pažįstate %(brand)s", + "Smileys & People": "Šypsenėlės ir Žmonės", + "%(count)s people|one": "%(count)s žmogus", + "%(count)s people|other": "%(count)s žmonės(-ų)", + "People": "Žmonės", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Žmonių ignoravimas atliekamas naudojant draudimų sąrašus, kuriuose yra taisyklės, nurodančios kas turi būti draudžiami. Užsiprenumeravus draudimų sąrašą, vartotojai/serveriai, užblokuoti šio sąrašo, bus nuo jūsų paslėpti.", + "Try out new ways to ignore people (experimental)": "Išbandykite naujus žmonių ignoravimo būdus (eksperimentiniai)", + "This will end the conference for everyone. Continue?": "Tai baigs konferenciją visiems. Tęsti?", + "End conference": "Baigti konferenciją", + "The call was answered on another device.": "Į skambutį buvo atsiliepta kitame įrenginyje.", + "Answered Elsewhere": "Atsiliepta Kitur", + "The call could not be established": "Nepavyko pradėti skambučio", + "The other party declined the call.": "Kita šalis atsisakė skambučio.", + "Call Declined": "Skambutis Atmestas" } From c676a0131e6bd0fd682dc20fe13a2e6776cb80cd Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Thu, 22 Oct 2020 09:40:58 +0000 Subject: [PATCH 0102/4306] Translated using Weblate (Albanian) Currently translated at 99.7% (2360 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 84a8ac2786..fcfd757abf 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2534,5 +2534,14 @@ "(connection failed)": "(dështoi lidhja)", "The call could not be established": "Thirrja s’u nis dot", "The other party declined the call.": "Pala tjetër hodhi poshtë thirrjen.", - "Call Declined": "Thirrja u Hodh Poshtë" + "Call Declined": "Thirrja u Hodh Poshtë", + "Move right": "Lëvize djathtas", + "Move left": "Lëvize majtas", + "Revoke permissions": "Shfuqizoji lejet", + "Unpin a widget to view it in this panel": "Që ta shihni te ku panel, shfiksojeni një widget", + "You can only pin up to %(count)s widgets|other": "Mundeni të fiksoni deri në %(count)s widget-e", + "Show Widgets": "Shfaqi Widget-et", + "Hide Widgets": "Fshihi Widget-et", + "The call was answered on another device.": "Thirrjes iu përgjigj në një tjetër pajisje.", + "Answered Elsewhere": "Përgjigjur Gjetkë" } From acbb0541213f4c3fea45ce4ffe5fcbaf8338d257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 21 Oct 2020 21:27:18 +0000 Subject: [PATCH 0103/4306] Translated using Weblate (Estonian) Currently translated at 100.0% (2365 of 2365 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index c782665da9..e29e1b6c85 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -2541,10 +2541,10 @@ "Call Declined": "Kõne on tagasilükatud", "Move right": "Liigu paremale", "Move left": "Liigu vasakule", - "Revoke permissions": "Tühista load", + "Revoke permissions": "Tühista õigused", "Unpin a widget to view it in this panel": "Sellel paneelil kuvamiseks eemaldage vidin lemmikutest", - "You can only pin up to %(count)s widgets|other": "Saab kinnitada ainult kuni %(count)s vidinaid", - "Show Widgets": "Kuva vidinad", + "You can only pin up to %(count)s widgets|other": "Sa saad kinnitada kuni %(count)s vidinat", + "Show Widgets": "Näita vidinaid", "Hide Widgets": "Peida vidinad", "The call was answered on another device.": "Kõnele vastati teises seadmes.", "Answered Elsewhere": "Vastatud mujal" From 173d79886544bc57c8de0b1ae4b16a346cd73bae Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Fri, 23 Oct 2020 18:41:24 +0100 Subject: [PATCH 0104/4306] added cheerio as explicit dep in package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0a3fd7a8b7..ca7d6ee0b7 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "html-entities": "^1.3.1", "is-ip": "^2.0.0", "katex": "^0.12.0", + "cheerio": "^1.0.0-rc.3", "linkifyjs": "^2.1.9", "lodash": "^4.17.19", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", From 06b20fad9543063409823540fcd4416a12c3ee21 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Fri, 23 Oct 2020 18:49:56 +0100 Subject: [PATCH 0105/4306] removed implicit "this" --- src/editor/serialize.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 88fd1c90fc..f31dd67ae7 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -68,10 +68,10 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = { _useHtmlParser2: true, decodeEntities: false }) // add fallback output for latex math, which should not be interpreted as markdown - phtml('div, span').each(function() { - const tex = phtml(this).attr('data-mx-maths') + phtml('div, span').each(function(i, e) { + const tex = phtml(e).attr('data-mx-maths') if (tex) { - phtml(this).html(`${tex}`) + phtml(e).html(`${tex}`) } }); return phtml.html(); From da60e4dba69d0475d837a55eb98293285fb116b0 Mon Sep 17 00:00:00 2001 From: Resynth Date: Sun, 25 Oct 2020 16:47:15 +0000 Subject: [PATCH 0106/4306] Lighten blockquote colour in dark mode Signed-off-by: Resynth --- res/themes/dark/css/_dark.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 6e0c9acdfe..df68bf0e2f 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -272,6 +272,10 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28); background-color: #080808; } } + + blockquote { + + } } // diff highlight colors From 2204e6c64e0042e0b937cf7d42e07816608e0234 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 25 Oct 2020 18:32:24 +0000 Subject: [PATCH 0107/4306] generate valid block html for commonmark spec --- src/editor/serialize.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index f31dd67ae7..bd7845315e 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -52,13 +52,17 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); - return `
`; + return `
\n\n
\n\n`; }); md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { const p1e = AllHtmlEntities.encode(p1); return ``; }); + + // make sure div tags always start on a new line, otherwise it will confuse + // the markdown parser + md = md.replace(/(.)
Date: Sun, 25 Oct 2020 20:55:59 +0000 Subject: [PATCH 0108/4306] Translated using Weblate (Esperanto) Currently translated at 99.6% (2359 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index a3a4ece6b3..2b96ac0969 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1479,7 +1479,7 @@ "Find a room…": "Trovi ĉambron…", "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas trovi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", "Explore rooms": "Esplori ĉambrojn", - "Add Email Address": "Aldoni retpoŝtadreson", + "Add Email Address": "Aldoni Retpoŝtadreson", "Add Phone Number": "Aldoni telefonnumeron", "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.": "Ĉi tiu ago bezonas atingi la norman identigan servilon por kontroli retpoŝtadreson aŭ telefonnumeron, sed la servilo ne havas uzokondiĉojn.", "Trust": "Fido", From e87071cc836f4216ebba612371f2a96203a06c12 Mon Sep 17 00:00:00 2001 From: Madison Scott-Clary Date: Sun, 25 Oct 2020 20:56:29 +0000 Subject: [PATCH 0109/4306] Translated using Weblate (Esperanto) Currently translated at 99.6% (2359 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 2b96ac0969..4fc9b5e11f 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1480,7 +1480,7 @@ "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas trovi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", "Explore rooms": "Esplori ĉambrojn", "Add Email Address": "Aldoni Retpoŝtadreson", - "Add Phone Number": "Aldoni telefonnumeron", + "Add Phone Number": "Aldoni Telefonumeron", "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.": "Ĉi tiu ago bezonas atingi la norman identigan servilon por kontroli retpoŝtadreson aŭ telefonnumeron, sed la servilo ne havas uzokondiĉojn.", "Trust": "Fido", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", From 84421efd46f1cb3cda022f8d5b14d86b551844d5 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sun, 25 Oct 2020 20:56:07 +0000 Subject: [PATCH 0110/4306] Translated using Weblate (Esperanto) Currently translated at 99.6% (2359 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 4fc9b5e11f..aa9dd157e0 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1479,7 +1479,7 @@ "Find a room…": "Trovi ĉambron…", "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas trovi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", "Explore rooms": "Esplori ĉambrojn", - "Add Email Address": "Aldoni Retpoŝtadreson", + "Add Email Address": "Aldoni retpoŝtadreson", "Add Phone Number": "Aldoni Telefonumeron", "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.": "Ĉi tiu ago bezonas atingi la norman identigan servilon por kontroli retpoŝtadreson aŭ telefonnumeron, sed la servilo ne havas uzokondiĉojn.", "Trust": "Fido", From e33f4ba9c0e8c29cf074f566cbb69c27007f6c57 Mon Sep 17 00:00:00 2001 From: Resynth Date: Sun, 25 Oct 2020 22:04:39 +0000 Subject: [PATCH 0111/4306] Warn on Access Token reveal Signed-off-by: Resynth --- .../views/dialogs/AccessTokenDialog.tsx | 39 +++++++++++++++++++ .../settings/tabs/user/HelpUserSettingsTab.js | 14 ++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/components/views/dialogs/AccessTokenDialog.tsx diff --git a/src/components/views/dialogs/AccessTokenDialog.tsx b/src/components/views/dialogs/AccessTokenDialog.tsx new file mode 100644 index 0000000000..81c48f219a --- /dev/null +++ b/src/components/views/dialogs/AccessTokenDialog.tsx @@ -0,0 +1,39 @@ + /* +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 React from 'react'; +import { _t } from '../../../languageHandler'; +import QuestionDialog from './QuestionDialog'; + +type IProps = Exclude< + React.ComponentProps, + "title" | "danger" | "description" + >; + +export default function AccessTokenDialog (props: IProps) { + return ( + + ); +} diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js index 85ba22a353..585a54ff86 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js @@ -27,6 +27,7 @@ import * as sdk from "../../../../../"; import PlatformPeg from "../../../../../PlatformPeg"; import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts"; import UpdateCheckButton from "../../UpdateCheckButton"; +import AccessTokenDialog from '../../../dialogs/AccessTokenDialog'; export default class HelpUserSettingsTab extends React.Component { static propTypes = { @@ -148,6 +149,17 @@ export default class HelpUserSettingsTab extends React.Component { ); } + onAccessTokenSpoilerClick = async (event) => { + // React throws away the event before we can use it (we are async, after all). + event.persist(); + + // We make the user accept a scary popup to combat Social Engineering. No peeking! + await Modal.createTrackedDialog('Reveal Access Token', '', AccessTokenDialog).finished; + + // Pass it onto the handler. + this._showSpoiler(event); + } + render() { const brand = SdkConfig.get().brand; @@ -266,7 +278,7 @@ export default class HelpUserSettingsTab extends React.Component { {_t("Homeserver is")} {MatrixClientPeg.get().getHomeserverUrl()}
{_t("Identity Server is")} {MatrixClientPeg.get().getIdentityServerUrl()}
{_t("Access Token:") + ' '} - <{ _t("click to reveal") }> From ae29168e077be47a83628f10b7765d6bcc9a7a0a Mon Sep 17 00:00:00 2001 From: Resynth Date: Sun, 25 Oct 2020 22:05:44 +0000 Subject: [PATCH 0112/4306] Lint Signed-off-by: Resynth --- src/components/views/dialogs/AccessTokenDialog.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/AccessTokenDialog.tsx b/src/components/views/dialogs/AccessTokenDialog.tsx index 81c48f219a..f95effd523 100644 --- a/src/components/views/dialogs/AccessTokenDialog.tsx +++ b/src/components/views/dialogs/AccessTokenDialog.tsx @@ -1,4 +1,4 @@ - /* +/* Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ type IProps = Exclude< "title" | "danger" | "description" >; -export default function AccessTokenDialog (props: IProps) { +export default function AccessTokenDialog(props: IProps) { return ( ); From 76edd551e5833cb1c94c1025fa069aeb79a9faa2 Mon Sep 17 00:00:00 2001 From: Resynth Date: Mon, 26 Oct 2020 00:34:14 +0000 Subject: [PATCH 0113/4306] Fix i18n Signed-off-by: Resynth --- 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 eda69d68ea..ad6593f704 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1585,6 +1585,7 @@ "Add a new server...": "Add a new server...", "%(networkName)s rooms": "%(networkName)s rooms", "Matrix rooms": "Matrix rooms", + "Do not reveal your Access Token to anyone, under any circumstances. Sharing your Access Token with someone would allow them to login to your account, and access your private information.": "Do not reveal your Access Token to anyone, under any circumstances. Sharing your Access Token with someone would allow them to login to your account, and access your private information.", "Matrix ID": "Matrix ID", "Matrix Room ID": "Matrix Room ID", "email address": "email address", From 7460e4cd66457d73cab76e409e163c94ff7fdd08 Mon Sep 17 00:00:00 2001 From: aWeinzierl Date: Sun, 25 Oct 2020 12:42:11 +0000 Subject: [PATCH 0114/4306] Translated using Weblate (German) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index b82fe80963..f1f6e6a74c 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2544,5 +2544,8 @@ "Answered Elsewhere": "Anderswo beantwortet", "The call could not be established": "Der Anruf konnte nicht hergestellt werden", "The other party declined the call.": "Die andere Seite hat den Anruf abgelehnt.", - "Call Declined": "Anruf abgelehnt" + "Call Declined": "Anruf abgelehnt", + "Data on this screen is shared with %(widgetDomain)s": "Daten auf diesem Bildschirm werden mit %(widgetDomain)s geteilt", + "Modal Widget": "Modales Widget", + "Offline encrypted messaging using dehydrated devices": "Offline verschlüsselte Kommunikation mit Hilfe von dehydrierten Geräten" } From 4e3e752287f1dfc08cfab3875c3bbf9d3841a7b3 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sat, 24 Oct 2020 18:12:54 +0000 Subject: [PATCH 0115/4306] Translated using Weblate (Hungarian) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index e7735dcf79..ef3b1e6789 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2538,5 +2538,16 @@ "(connection failed)": "(kapcsolódás sikertelen)", "The call could not be established": "A hívás kapcsolatot nem lehet felépíteni", "The other party declined the call.": "A másik fél elutasította a hívást.", - "Call Declined": "Hívás elutasítva" + "Call Declined": "Hívás elutasítva", + "Move right": "Jobbra mozgat", + "Move left": "Balra mozgat", + "Revoke permissions": "Jogosultságok visszavonása", + "Data on this screen is shared with %(widgetDomain)s": "Az adatok erről a képernyőről megosztásra kerülnek ezzel: %(widgetDomain)s", + "Modal Widget": "Előugró kisalkalmazás", + "Unpin a widget to view it in this panel": "Kisalkalmazás megjelenítése ezen a panelen", + "You can only pin up to %(count)s widgets|other": "Csak %(count)s kisalkalmazást tudsz kitűzni", + "Show Widgets": "Kisalkalmazások megjelenítése", + "Hide Widgets": "Kisalkalmazások elrejtése", + "The call was answered on another device.": "A hívás másik eszközön lett fogadva.", + "Answered Elsewhere": "Máshol lett felvéve" } From d3840438a9f0e043457f520fd695f993e544fe67 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 23 Oct 2020 14:52:34 +0000 Subject: [PATCH 0116/4306] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 2cae187676..32535d3805 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -2478,5 +2478,7 @@ "Show Widgets": "Mostrar widgets", "Hide Widgets": "Esconder widgets", "The call was answered on another device.": "A chamada foi atendida em outro aparelho.", - "Answered Elsewhere": "Atendida em algum lugar" + "Answered Elsewhere": "Respondido em algum lugar", + "Modal Widget": "Popup do widget", + "Data on this screen is shared with %(widgetDomain)s": "Dados nessa tela são compartilhados com %(widgetDomain)s" } From 5ab899fda65ea082722932b450af7b5191c7e809 Mon Sep 17 00:00:00 2001 From: progserega Date: Sun, 25 Oct 2020 11:21:57 +0000 Subject: [PATCH 0117/4306] Translated using Weblate (Russian) Currently translated at 99.8% (2363 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 70a96a87bf..cc3f4abe5d 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -98,7 +98,7 @@ "click to reveal": "нажмите для открытия", "%(senderName)s invited %(targetName)s.": "%(senderName)s пригласил %(targetName)s.", "%(targetName)s joined the room.": "%(targetName)s вошёл в комнату.", - "%(senderName)s kicked %(targetName)s.": "%(senderName)s выгнал(а) %(targetName)s.", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s исключил(а) %(targetName)s.", "%(targetName)s left the room.": "%(targetName)s покинул(а) комнату.", "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s сделал(а) историю разговора видимой для всех собеседников с момента их приглашения.", "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s сделал(а) историю разговора видимой для всех собеседников с момента их входа в комнату.", From ed250b0abf7acc8b3eea0510f105c227aa19cf03 Mon Sep 17 00:00:00 2001 From: Nikita Epifanov Date: Sun, 25 Oct 2020 11:17:45 +0000 Subject: [PATCH 0118/4306] Translated using Weblate (Russian) Currently translated at 99.8% (2363 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index cc3f4abe5d..21f89218c3 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -2522,5 +2522,28 @@ "Call connecting...": "Устанавливается соединение…", "Starting camera...": "Запуск камеры…", "Starting microphone...": "Запуск микрофона…", - "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Все серверы запрещены к участию! Эта комната больше не может быть использована." + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Все серверы запрещены к участию! Эта комната больше не может быть использована.", + "Remove messages sent by others": "Удалить сообщения, отправленные другими", + "Offline encrypted messaging using dehydrated devices": "Автономный обмен зашифрованными сообщениями с сохраненными устройствами", + "Move right": "Сдвинуть вправо", + "Move left": "Сдвинуть влево", + "Revoke permissions": "Отозвать разрешения", + "Data on this screen is shared with %(widgetDomain)s": "Данные на этом экране используются %(widgetDomain)s", + "Modal Widget": "Модальный виджет", + "Ignored attempt to disable encryption": "Игнорируемая попытка отключить шифрование", + "Unpin a widget to view it in this panel": "Открепите виджет, чтобы просмотреть его на этой панели", + "You can only pin up to %(count)s widgets|other": "Вы можете закрепить не более %(count)s виджетов", + "Show Widgets": "Показать виджеты", + "Hide Widgets": "Скрыть виджеты", + "%(senderName)s declined the call.": "%(senderName)s отклонил(а) вызов.", + "(an error occurred)": "(произошла ошибка)", + "(their device couldn't start the camera / microphone)": "(их устройство не может запустить камеру / микрофон)", + "(connection failed)": "(ошибка соединения)", + "%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s изменил(а) серверные разрешения для этой комнаты.", + "%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s устанавливает серверные разрешения для этой комнаты.", + "The call was answered on another device.": "На звонок ответили на другом устройстве.", + "Answered Elsewhere": "Ответил в другом месте", + "The call could not be established": "Звонок не может быть установлен", + "The other party declined the call.": "Другой абонент отклонил звонок.", + "Call Declined": "Вызов отклонён" } From 0ff396e9eced1bc8a829526bf7e9ddac105202dd Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Sun, 25 Oct 2020 12:44:21 +0000 Subject: [PATCH 0119/4306] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index c6c800b142..7d2c7467b0 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2550,5 +2550,7 @@ "Show Widgets": "顯示小工具", "Hide Widgets": "隱藏小工具", "The call was answered on another device.": "通話已在其他裝置上回應。", - "Answered Elsewhere": "在其他地方回答" + "Answered Elsewhere": "在其他地方回答", + "Data on this screen is shared with %(widgetDomain)s": "在此畫面上的資料會與 %(widgetDomain)s 分享", + "Modal Widget": "程式小工具" } From 7cab8a37b9a87498eb00a092725322c3f4664cac Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sun, 25 Oct 2020 20:56:46 +0000 Subject: [PATCH 0120/4306] Translated using Weblate (Esperanto) Currently translated at 99.6% (2359 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index aa9dd157e0..a3a4ece6b3 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1480,7 +1480,7 @@ "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas trovi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", "Explore rooms": "Esplori ĉambrojn", "Add Email Address": "Aldoni retpoŝtadreson", - "Add Phone Number": "Aldoni Telefonumeron", + "Add Phone Number": "Aldoni telefonnumeron", "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.": "Ĉi tiu ago bezonas atingi la norman identigan servilon por kontroli retpoŝtadreson aŭ telefonnumeron, sed la servilo ne havas uzokondiĉojn.", "Trust": "Fido", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", From 72d7760ed2204ee747618192096da439b807664c Mon Sep 17 00:00:00 2001 From: Dominik George Date: Fri, 23 Oct 2020 14:58:29 +0000 Subject: [PATCH 0121/4306] =?UTF-8?q?Translated=20using=20Weblate=20(Norwe?= =?UTF-8?q?gian=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 57.7% (1366 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/nb_NO/ --- src/i18n/strings/nb_NO.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/nb_NO.json b/src/i18n/strings/nb_NO.json index f8f0d890be..6388983f77 100644 --- a/src/i18n/strings/nb_NO.json +++ b/src/i18n/strings/nb_NO.json @@ -1422,8 +1422,8 @@ "Compact": "Kompakt", "Modern": "Moderne", "Server or user ID to ignore": "Tjener- eller bruker-ID-en som skal ignoreres", - "Show %(count)s more|other": "Vis %(count) til", - "Show %(count)s more|one": "Vis %(count) til", + "Show %(count)s more|other": "Vis %(count)s til", + "Show %(count)s more|one": "Vis %(count)s til", "Use default": "Bruk standarden", "Notification options": "Varselsinnstillinger", "Room options": "Rominnstillinger", From 159d4ac9730a25b3eebb60781fa4891601b6cdf3 Mon Sep 17 00:00:00 2001 From: XoseM Date: Sat, 24 Oct 2020 03:37:54 +0000 Subject: [PATCH 0122/4306] Translated using Weblate (Galician) Currently translated at 99.9% (2366 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 57fc1f645d..91a8a24c56 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -2546,5 +2546,7 @@ "Show Widgets": "Mostrar Widgets", "Hide Widgets": "Agochar Widgets", "The call was answered on another device.": "A chamada foi respondida noutro dispositivo.", - "Answered Elsewhere": "Respondido noutro lugar" + "Answered Elsewhere": "Respondido noutro lugar", + "Data on this screen is shared with %(widgetDomain)s": "Os datos nesta pantalla compártense con %(widgetDomain)s", + "Modal Widget": "Widget modal" } From 5555931afa037da3ce44dda3f7e74a9a9ccd2341 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Mon, 26 Oct 2020 14:48:49 +0000 Subject: [PATCH 0123/4306] Translated using Weblate (Lithuanian) Currently translated at 72.4% (1714 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 51 +++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index c1d450bdef..2bd9145483 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -220,7 +220,7 @@ "Changes your display nickname": "Pakeičia jūsų rodomą slapyvardį", "Invites user with given id to current room": "Pakviečia naudotoją su nurodytu id į esamą kambarį", "You are now ignoring %(userId)s": "Dabar ignoruojate %(userId)s", - "Opens the Developer Tools dialog": "Atveria programuotojo įrankių dialogą", + "Opens the Developer Tools dialog": "Atveria Programuotojo Įrankių dialogą", "Verified key": "Patvirtintas raktas", "Displays action": "Rodo veiksmą", "Reason": "Priežastis", @@ -229,9 +229,9 @@ "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s pakeitė savo rodomą vardą į %(displayName)s.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s nustatė savo rodomą vardą į %(displayName)s.", "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s pašalino savo rodomą vardą (%(oldDisplayName)s).", - "%(senderName)s removed their profile picture.": "%(senderName)s pašalino savo profilio paveikslą.", - "%(senderName)s changed their profile picture.": "%(senderName)s pakeitė savo profilio paveikslą.", - "%(senderName)s set a profile picture.": "%(senderName)s nustatė profilio paveikslą.", + "%(senderName)s removed their profile picture.": "%(senderName)s pašalino savo profilio paveikslėlį.", + "%(senderName)s changed their profile picture.": "%(senderName)s pakeitė savo profilio paveikslėlį.", + "%(senderName)s set a profile picture.": "%(senderName)s nustatė profilio paveikslėlį.", "%(targetName)s rejected the invitation.": "%(targetName)s atmetė pakvietimą.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s pakeitė temą į \"%(topic)s\".", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s pakeitė kambario pavadinimą į %(roomName)s.", @@ -446,7 +446,7 @@ "%(senderName)s kicked %(targetName)s.": "%(senderName)s išmetė %(targetName)s.", "(not supported by this browser)": "(nėra palaikoma šios naršyklės)", "(no answer)": "(nėra atsakymo)", - "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s padarė būsimą kambario istoriją matomą visiems kambario dalyviams, nuo pat jų pakvietimo.", + "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s padarė būsimą kambario istoriją matomą visiems kambario dalyviams, nuo jų pakvietimo momento.", "%(senderName)s made future room history visible to all room members.": "%(senderName)s padarė būsimą kambario istoriją matomą visiems kambario dalyviams.", "%(senderName)s made future room history visible to anyone.": "%(senderName)s padarė būsimą kambario istoriją matomą bet kam.", "Your browser does not support the required cryptography extensions": "Jūsų naršyklė nepalaiko reikalingų kriptografijos plėtinių", @@ -733,8 +733,8 @@ "Define the power level of a user": "Nustatykite vartotojo galios lygį", "Deops user with given id": "Deop'ina vartotoją su nurodytu id", "Adds a custom widget by URL to the room": "Į kambarį prideda pasirinktinį valdiklį pagal URL", - "Please supply a https:// or http:// widget URL": "Prašome pateikti https:// arba http:// valdiklio URL", - "You cannot modify widgets in this room.": "Jūs negalite keisti valdiklių šiame kambaryje.", + "Please supply a https:// or http:// widget URL": "Pateikite https:// arba http:// valdiklio URL", + "You cannot modify widgets in this room.": "Jūs negalite modifikuoti valdiklių šiame kambaryje.", "Forces the current outbound group session in an encrypted room to be discarded": "Priverčia išmesti esamą užsibaigiantį grupės seansą užšifruotame kambaryje", "Sends the given message coloured as a rainbow": "Išsiunčia nurodytą žinutę nuspalvintą kaip vaivorykštė", "Sends the given emote coloured as a rainbow": "Išsiunčia nurodytą emociją nuspalvintą kaip vaivorykštė", @@ -746,16 +746,16 @@ "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s atnaujino šį kambarį.", "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s padarė kambarį viešą visiems žinantiems nuorodą.", "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s padarė kambarį tik pakviestiems.", - "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s pakeitė prisijungimo normą į %(rule)s", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s pakeitė prisijungimo taisyklę į %(rule)s", "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s leido svečiams prisijungti prie kambario.", "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s uždraudė svečiams prisijungti prie kambario.", "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s pakeitė svečių prieigą prie %(rule)s", - "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s įjungė ženkliukus bendruomenėi %(groups)s šiame kambaryje.", - "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s išjungė ženkliukus bendruomenėi %(groups)s šiame kambaryje.", - "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s įjungė ženkliukus bendruomenėi %(newGroups)s ir išjungė ženkliukus bendruomenėi %(oldGroups)s šiame kambaryje.", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s įjungė ženkliukus bendruomenei %(groups)s šiame kambaryje.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s išjungė ženkliukus bendruomenei %(groups)s šiame kambaryje.", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s įjungė ženkliukus bendruomenei %(newGroups)s ir išjungė ženkliukus bendruomenei %(oldGroups)s šiame kambaryje.", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s atšaukė pakvietimą %(targetDisplayName)s prisijungti prie kambario.", "%(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 all room members, from the point they joined.": "%(senderName)s padarė būsimą kambario istoriją matomą visiems kambario dalyviams, nuo jų prisijungimo momento.", "%(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 galios lygį iš %(fromPowerLevel)s į %(toPowerLevel)s", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s pakeitė %(powerLevelDiffText)s.", @@ -1708,7 +1708,7 @@ "about a day ago": "maždaug prieš dieną", "about an hour ago": "maždaug prieš valandą", "about a minute ago": "maždaug prieš minutę", - "Displays information about a user": "Rodo informaciją apie vartotoją", + "Displays information about a user": "Parodo informaciją apie vartotoją", "Error whilst fetching joined communities": "Gaunant prisijungtas bendruomenes įvyko klaida", "Who can join this community?": "Kas gali prisijungti prie šios bendruomenės?", "Join this community": "Prisijungti prie šios bendruomenės", @@ -1776,5 +1776,28 @@ "Answered Elsewhere": "Atsiliepta Kitur", "The call could not be established": "Nepavyko pradėti skambučio", "The other party declined the call.": "Kita šalis atsisakė skambučio.", - "Call Declined": "Skambutis Atmestas" + "Call Declined": "Skambutis Atmestas", + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Visiems serveriams uždrausta dalyvauti! Šis kambarys nebegali būti naudojamas.", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s pašalino draudimo taisyklę, sutampančią su %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s pašalino taisyklę, draudžiančią serverius, sutampančius su %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s pašalino taisyklę, draudžiančią kambarius, sutampančius su %(glob)s", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s pašalino taisyklę, draudžiančią vartotojus, sutampančius su %(glob)s", + "%(senderName)s updated an invalid ban rule": "%(senderName)s atnaujino klaidingą draudimo taisyklę", + "%(senderName)s declined the call.": "%(senderName)s atmetė skambutį.", + "(an error occurred)": "(įvyko klaida)", + "(their device couldn't start the camera / microphone)": "(jų įrenginys negalėjo pradėti kameros / mikrofono)", + "(connection failed)": "(prisijungimas nepavyko)", + "%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s pakeitė serverio prieigos kontrolės sąrašus šiam kambariui.", + "%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s nustatė serverio prieigos kontrolės sąrašus šiam kambariui.", + "Sends a message to the given user": "Siunčia žinutę nurodytam vartotojui", + "Opens chat with the given user": "Atidaro pokalbį su nurodytu vartotoju", + "Send a bug report with logs": "Siųsti pranešimą apie klaidą kartu su žurnalu", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Jūsų pateiktas pasirašymo raktas sutampa su pasirašymo raktu, gautu iš vartotojo %(userId)s seanso %(deviceId)s. Seansas pažymėtas kaip patikrintas.", + "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!": "ĮSPĖJIMAS: RAKTŲ PATIKRINIMAS NEPAVYKO! Pasirašymo raktas vartotojui %(userId)s ir seansui %(deviceId)s yra \"%(fprint)s\", kuris nesutampa su pateiktu raktu \"%(fingerprint)s\". Tai gali reikšti, kad jūsų komunikacijos yra perimamos!", + "Unknown (user, session) pair:": "Nežinoma (vartotojo, seanso) pora:", + "Verifies a user, session, and pubkey tuple": "Patvirtina vartotojo, seanso ir pubkey daugiadalę duomenų struktūrą", + "Please supply a widget URL or embed code": "Pateikite valdiklio URL arba įterpimo kodą", + "Could not find user in room": "Vartotojo rasti kambaryje nepavyko", + "Command failed": "Komanda nepavyko", + "Unrecognised room address:": "" } From 2beeb493b7d7bfa2436a3b1e21bc70c345251452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Fri, 23 Oct 2020 21:02:24 +0000 Subject: [PATCH 0124/4306] Translated using Weblate (Estonian) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index e29e1b6c85..16c9fdbb8f 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -2547,5 +2547,7 @@ "Show Widgets": "Näita vidinaid", "Hide Widgets": "Peida vidinad", "The call was answered on another device.": "Kõnele vastati teises seadmes.", - "Answered Elsewhere": "Vastatud mujal" + "Answered Elsewhere": "Vastatud mujal", + "Data on this screen is shared with %(widgetDomain)s": "Andmeid selles vaates jagatakse %(widgetDomain)s serveriga", + "Modal Widget": "Modaalne vidin" } From 19395f3c3cf6076b097553da7873a045721d0ff9 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 26 Oct 2020 16:37:45 +0100 Subject: [PATCH 0125/4306] null checks added --- src/components/structures/RoomView.tsx | 16 ++++++++-------- .../views/elements/effects/effectUtilities.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 1a18ece008..57c9afb17b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -831,14 +831,14 @@ export default class RoomView extends React.Component { }; private handleEffects = (ev) => { - if (this.state.room.getUnreadNotificationCount() === 0) return; - if (this.state.matrixClientIsReady) { - effects.forEach(effect => { - if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { - dis.dispatch({action: `effects.${effect.command}`}); - } - }) - } + if (!this.state.room || + !this.state.matrixClientIsReady || + this.state.room.getUnreadNotificationCount() === 0) return; + effects.forEach(effect => { + if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { + dis.dispatch({action: `effects.${effect.command}`}); + } + }) }; private onRoomName = (room: Room) => { diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts index 212c477b39..e94287c745 100644 --- a/src/components/views/elements/effects/effectUtilities.ts +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -4,5 +4,5 @@ * @param {Array} emojis The list of emojis to check for */ export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { - return emojis.some((emoji) => content.body.includes(emoji)); + return emojis.some((emoji) => content.body && content.body.includes(emoji)); } From 40881a73596374ec3e4c845d227555c26b5995cd Mon Sep 17 00:00:00 2001 From: MamasLT Date: Mon, 26 Oct 2020 15:54:37 +0000 Subject: [PATCH 0126/4306] Translated using Weblate (Lithuanian) Currently translated at 74.0% (1752 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 70 +++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 2bd9145483..ad050d6e31 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1,7 +1,7 @@ { "This email address is already in use": "Šis el. pašto adresas jau naudojamas", "This phone number is already in use": "Šis telefono numeris jau naudojamas", - "Failed to verify email address: make sure you clicked the link in the email": "Nepavyko patvirtinti el. pašto adreso: įsitikinkite, kad paspaudėte nuorodą el. laiške", + "Failed to verify email address: make sure you clicked the link in the email": "Nepavyko patikrinti el. pašto adreso: įsitikinkite, kad paspaudėte nuorodą el. laiške", "The platform you're on": "Jūsų naudojama platforma", "The version of %(brand)s": "%(brand)s versija", "Your language of choice": "Jūsų pasirinkta kalba", @@ -113,7 +113,7 @@ "What's new?": "Kas naujo?", "Notify me for anything else": "Pranešti man apie visa kita", "View Source": "Peržiūrėti šaltinį", - "Close": "Užverti", + "Close": "Uždaryti", "Can't update user notification settings": "Nepavyksta atnaujinti naudotojo pranešimų nustatymų", "Notify for all other messages/rooms": "Pranešti apie visas kitas žinutes/pokalbių kambarius", "Unable to look up room ID from server": "Nepavyko gauti kambario ID iš serverio", @@ -544,7 +544,7 @@ "Missing roomId.": "Trūksta kambario ID.", "Leave room": "Išeiti iš kambario", "(could not connect media)": "(nepavyko prijungti medijos)", - "This homeserver has hit its Monthly Active User limit.": "Šis serveris pasiekė savo mėnesinį aktyvių naudotojų limitą.", + "This homeserver has hit its Monthly Active User limit.": "Šis serveris pasiekė savo Mėnesinį Aktyvių Vartotojų 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": "Įjungti valdiklių ekrano kopijas palaikomuose valdikliuose", @@ -599,7 +599,7 @@ "User %(user_id)s does not exist": "Vartotojas %(user_id)s neegzistuoja", "Unknown server error": "Nežinoma serverio klaida", "Avoid sequences": "Venkite sekų", - "Avoid recent years": "Venkite paskiausių metų", + "Avoid recent years": "Venkite pastarųjų metų", "Avoid years that are associated with you": "Venkite su jumis susijusių metų", "Avoid dates and years that are associated with you": "Venkite su jumis susijusių metų ir datų", "Capitalization doesn't help very much": "Rašymas didžiosiomis raidėmis nelabai padeda", @@ -611,7 +611,7 @@ "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": "Tokius pasikartojimus kaip \"abcabcabc\" yra tik truputėlį sunkiau atspėti nei \"abc\"", "Sequences like abc or 6543 are easy to guess": "Tokias sekas kaip \"abc\" ar \"6543\" yra lengva atspėti", "Recent years are easy to guess": "Paskiausius metus yra lengva atspėti", - "Dates are often easy to guess": "Datas, dažniausiai, yra lengva atspėti", + "Dates are often easy to guess": "Datas dažniausiai yra lengva atspėti", "This is a top-10 common password": "Tai yra vienas iš 10 dažniausiai naudojamų slaptažodžių", "This is a top-100 common password": "Tai yra vienas iš 100 dažniausiai naudojamų slaptažodžių", "This is a very common password": "Tai yra labai dažnai naudojamas slaptažodis", @@ -760,7 +760,7 @@ "%(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 …|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 …", "%(names)s and %(lastPerson)s are typing …": "%(names)s ir %(lastPerson)s rašo …", "Cannot reach homeserver": "Serveris nepasiekiamas", @@ -771,16 +771,16 @@ "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.": "Jūs galite iš naujo nustatyti savo slaptažodį, tačiau kai kurios funkcijos bus nepasiekiamos, kol tapatybės serveris prisijungs. Jei ir toliau matote šį įspėjimą, patikrinkite savo konfigūraciją arba susisiekite su serverio administratoriumi.", "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.": "Jūs galite prisijungti, tačiau kai kurios funkcijos bus nepasiekiamos, kol tapatybės serveris prisijungs. Jei ir toliau matote šį įspėjimą, patikrinkite savo konfigūraciją arba susisiekite su serverio administratoriumi.", "No homeserver URL provided": "Nepateiktas serverio URL", - "Unexpected error resolving homeserver configuration": "Netikėta klaida nustatant serverio konfigūraciją", - "Unexpected error resolving identity server configuration": "Netikėta klaida nustatant tapatybės serverio konfigūraciją", - "%(items)s and %(count)s others|other": "%(items)s ir %(count)s kiti", + "Unexpected error resolving homeserver configuration": "Netikėta klaida nusistatant serverio konfigūraciją", + "Unexpected error resolving identity server configuration": "Netikėta klaida nusistatant tapatybės serverio konfigūraciją", + "%(items)s and %(count)s others|other": "%(items)s ir %(count)s kiti(-ų)", "%(items)s and %(count)s others|one": "%(items)s ir dar vienas", "%(items)s and %(lastItem)s": "%(items)s ir %(lastItem)s", "Unrecognised address": "Neatpažintas adresas", "You do not have permission to invite people to this room.": "Jūs neturite leidimo pakviesti žmones į šį kambarį.", "User %(userId)s is already in the room": "Vartotojas %(userId)s jau yra kambaryje", "User %(user_id)s may or may not exist": "Vartotojas %(user_id)s gali ir neegzistuoti", - "The user must be unbanned before they can be invited.": "Norint pakviesti vartotoją, pirmiausia turi būti pašalintas draudimas.", + "The user must be unbanned before they can be invited.": "Norint pakviesti vartotoją, prieš tai reikia pašalinti jo draudimą.", "The user's homeserver does not support the version of the room.": "Vartotojo serveris nepalaiko kambario versijos.", "Use a longer keyboard pattern with more turns": "Naudokite ilgesnį klaviatūros modelį su daugiau vijų", "There was an error joining the room": "Prisijungiant prie kambario įvyko klaida", @@ -1077,7 +1077,7 @@ "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": "Patikrinti", "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", @@ -1362,7 +1362,7 @@ "Almost there! Is %(displayName)s showing the same shield?": "Beveik atlikta! Ar %(displayName)s rodo tokį patį skydą?", "No": "Ne", "Yes": "Taip", - "Interactively verify by Emoji": "Patvirtinti interaktyviai, naudojant jaustukus", + "Interactively verify by Emoji": "Patvirtinti interaktyviai, naudojant Jaustukus", "The message you are trying to send is too large.": "Žinutė, kurią jūs bandote siųsti, yra per didelė.", "Show a placeholder for removed messages": "Rodyti pašalintų žinučių žymeklį", "Show join/leave messages (invites/kicks/bans unaffected)": "Rodyti prisijungimo/išėjimo žinutes (pakvietimai/išmetimai/draudimai nepaveikti)", @@ -1381,7 +1381,7 @@ "Encrypted messages in one-to-one chats": "Šifruotos žinutės privačiuose pokalbiuose", "When rooms are upgraded": "Kai atnaujinami kambariai", "My Ban List": "Mano Draudimų Sąrašas", - "This is your list of users/servers you have blocked - don't leave the room!": "Tai jūsų užblokuotų vartotojų/serverių sąrašas - nepalikite kambario!", + "This is your list of users/servers you have blocked - don't leave the room!": "Tai yra jūsų užblokuotų vartotojų/serverių sąrašas - neišeikite iš kambario!", "Verify this user by confirming the following emoji appear on their screen.": "Patvirtinkite šį vartotoją, įsitikindami, kad šie jaustukai rodomi jo ekrane.", "⚠ These settings are meant for advanced users.": "⚠ Šie nustatymai yra skirti pažengusiems vartotojams.", "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Jūsų asmeniniame draudimų sąraše yra visi vartotojai/serveriai, iš kurių jūs asmeniškai nenorite matyti pranešimų. Po pirmojo jūsų vartotojo/serverio ignoravimo, jūsų kambarių sąraše pasirodys naujas kambarys pavadinimu 'Mano Draudimų Sąrašas' - likite šiame kambaryje, kad draudimų sąrašas veiktų.", @@ -1419,7 +1419,7 @@ "Failed to reject invitation": "Nepavyko atmesti pakvietimo", "Failed to leave room": "Nepavyko išeiti iš kambario", "Can't leave Server Notices room": "Negalima išeiti iš Serverio Pranešimų kambario", - "This room is used for important messages from the Homeserver, so you cannot leave it.": "Šis kambarys yra naudojamas svarbioms žinutėms iš serverio, todėl jūs negalite jo palikti.", + "This room is used for important messages from the Homeserver, so you cannot leave it.": "Šis kambarys yra naudojamas svarbioms žinutėms iš serverio, tad jūs negalite iš jo išeiti.", "Terms and Conditions": "Taisyklės ir Sąlygos", "Self-verification request": "Savarankiško patvirtinimo užklausa", "Logout": "Atsijungti", @@ -1438,7 +1438,7 @@ "Use your account or create a new one to continue.": "Norėdami tęsti naudokite savo paskyrą arba sukurkite naują.", "Create Account": "Sukurti Paskyrą", "Custom (%(level)s)": "Pasirinktinis (%(level)s)", - "Ask this user to verify their session, or manually verify it below.": "Paprašykite šio vartotojo patvirtinti savo seansą arba patvirtinkite jį rankiniu būdu žemiau.", + "Ask this user to verify their session, or manually verify it below.": "Paprašykite šio vartotojo patvirtinti savo seansą, arba patvirtinkite jį rankiniu būdu žemiau.", "Encryption upgrade available": "Galimas šifravimo atnaujinimas", "Verify this user by confirming the following number appears on their screen.": "Patvirtinkite šį vartotoją įsitikindami, kad jo ekrane rodomas toliau esantis skaičius.", "Backup has a signature from unknown user with ID %(deviceId)s": "Atsarginė kopija turi nežinomo vartotojo parašą su ID %(deviceId)s", @@ -1799,5 +1799,43 @@ "Please supply a widget URL or embed code": "Pateikite valdiklio URL arba įterpimo kodą", "Could not find user in room": "Vartotojo rasti kambaryje nepavyko", "Command failed": "Komanda nepavyko", - "Unrecognised room address:": "" + "Unrecognised room address:": "", + "You signed in to a new session without verifying it:": "Jūs prisijungėte prie naujo seanso jo nepatikrinę:", + "You can also set up Secure Backup & manage your keys in Settings.": "Jūs taip pat galite nustatyti Saugią Atsarginę Kopiją ir tvarkyti savo raktus Nustatymuose.", + "Secure Backup": "Saugi Atsarginė Kopija", + "Set up Secure Backup": "Nustatyti Saugią Atsarginę Kopiją", + "Ok": "Gerai", + "Contact your server admin.": "Susisiekite su savo serverio administratoriumi.", + "I want to help": "Aš noriu padėti", + "Send anonymous usage data which helps us improve %(brand)s. This will use a cookie.": "Siųsti anoniminius naudojimo duomenis, kurie padeda mums tobulinti %(brand)s. Tam bus naudojamas slapukas.", + "Help us improve %(brand)s": "Padėkite mums tobulinti %(brand)s", + "Unknown App": "Nežinoma Programa", + "Error leaving room": "Klaida išeinant iš kambario", + "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Šio vartotojo deaktyvavimas atjungs juos ir neleis jiems vėl prisijungti atgal. Taip pat jie išeis iš visų kambarių, kuriuose jie yra. Šis veiksmas negali būti atšauktas. Ar tikrai norite deaktyvuoti šį vartotoją?", + "Unexpected server error trying to leave the room": "Netikėta serverio klaida bandant išeiti iš kambario", + "%(num)s days from now": "%(num)s dienas(-ų) nuo dabar", + "%(num)s hours from now": "%(num)s valandas(-ų) nuo dabar", + "%(num)s minutes from now": "%(num)s minutes(-ų) nuo dabar", + "a few seconds from now": "keletą sekundžių nuo dabar", + "%(num)s days ago": "prieš %(num)s dienas(-ų)", + "%(num)s hours ago": "prieš %(num)s valandas(-ų)", + "%(num)s minutes ago": "prieš %(num)s minutes(-ų)", + "a few seconds ago": "prieš kelias sekundes", + "Manually Verify by Text": "Patvirtinti rankiniu būdu, naudojant Tekstą", + "Not Trusted": "Nepatikimas", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) prisijungė prie naujo seanso jo nepatvirtinę:", + "Dark": "Tamsi", + "Light": "Šviesi", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s atnaujino draudimo taisyklę, kuri sutapo su %(oldGlob)s į sutampančią su %(newGlob)s dėl %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s pakeitė taisyklę, kuri draudė serverius, sutampančius su %(oldGlob)s į sutampančius su %(newGlob)s dėl %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s pakeitė taisyklę, kuri draudė kambarius, sutampančius su %(oldGlob)s į sutampančius su %(newGlob)s dėl %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s pakeitė taisyklę, kuri draudė vartotojus, sutampančius su %(oldGlob)s į sutampančius su %(newGlob)s dėl %(reason)s", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s sukūrė draudimo taisyklę, sutampančią su %(glob)s dėl %(reason)s", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s sukūrė taisyklę, draudžiančią serverius, sutampančius su %(glob)s dėl %(reason)s", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s sukūrė taisyklę, draudžiančią kambarius, sutampančius su %(glob)s dėl %(reason)s", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s sukūrė taisyklę, draudžiančią vartotojus, sutampančius su %(glob)s dėl %(reason)s", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s atnaujino draudimo taisyklę, sutampančią su %(glob)s dėl %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią serverius, sutampančius su %(glob)s dėl %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią kambarius, sutampančius su %(glob)s dėl %(reason)s", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią vartotojus, sutampančius su %(glob)s dėl %(reason)s" } From dc28616a6f366af8cf34970ce363d2e31fe74555 Mon Sep 17 00:00:00 2001 From: Resynth Date: Mon, 26 Oct 2020 22:53:37 +0000 Subject: [PATCH 0127/4306] Remove empty CSS block Signed-off-by: Resynth --- res/themes/dark/css/_dark.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index df68bf0e2f..6e0c9acdfe 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -272,10 +272,6 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28); background-color: #080808; } } - - blockquote { - - } } // diff highlight colors From a3212c0477ec0fc9166a56a49a9579bae4712d5f Mon Sep 17 00:00:00 2001 From: Resynth Date: Mon, 26 Oct 2020 23:46:53 +0000 Subject: [PATCH 0128/4306] Fix weird formatting Signed-off-by: Resynth --- src/components/views/dialogs/AccessTokenDialog.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/AccessTokenDialog.tsx b/src/components/views/dialogs/AccessTokenDialog.tsx index f95effd523..2d96d8ec20 100644 --- a/src/components/views/dialogs/AccessTokenDialog.tsx +++ b/src/components/views/dialogs/AccessTokenDialog.tsx @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd +Copyright 2020 Resynth Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,7 +22,7 @@ import QuestionDialog from './QuestionDialog'; type IProps = Exclude< React.ComponentProps, "title" | "danger" | "description" - >; +>; export default function AccessTokenDialog(props: IProps) { return ( From 432deb665d9d8e14d26bd9f4b35234404f5e6ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corentin=20No=C3=ABl?= Date: Mon, 26 Oct 2020 21:34:41 +0000 Subject: [PATCH 0129/4306] Translated using Weblate (French) Currently translated at 94.8% (2245 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 42eead2057..5598568948 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2395,5 +2395,22 @@ "Cross-signing is ready for use, but secret storage is currently not being used to backup your keys.": "La signature croisée est prête à l'emploi, mais le coffre secret n'est pas actuellement utilisé pour sauvegarder vos clés.", "Master private key:": "Clé privée maîtresse :", "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.": "%(brand)s ne peut actuellement mettre en cache vos messages chiffrés localement de manière sécurisée via le navigateur Web. Utilisez %(brand)s Desktop pour que les messages chiffrés apparaissent dans vos résultats de recherche.", - "There are advanced notifications which are not shown here.": "Des notifications avancées ne sont pas affichées ici." + "There are advanced notifications which are not shown here.": "Des notifications avancées ne sont pas affichées ici.", + "ready": "prêt", + "The operation could not be completed": "L'opération n'a pas pu être terminée", + "Failed to save your profile": "Erreur lors de l'enregistrement du profile", + "Unknown App": "Application inconnue", + "%(senderName)s declined the call.": "%(senderName)s a refusé l'appel.", + "(an error occurred)": "(une erreur est survenue)", + "(connection failed)": "(échec de connexion)", + "%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s a changé les paramètres d'accès du serveur pour ce salon.", + "%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s a défini les paramètres d'accès du serveur pour ce salon.", + "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Ajoute ( ͡° ͜ʖ ͡°) devant un message en texte brut", + "This will end the conference for everyone. Continue?": "Ceci arrêtera la téléconférence pour tout le monde. Continuer ?", + "End conference": "Finir la téléconférence", + "The call was answered on another device.": "L'appel a été répondu sur un autre appareil.", + "Answered Elsewhere": "Répondu autre-part", + "The call could not be established": "L'appel n'a pas pu être établi", + "The other party declined the call.": "L'autre personne a décliné l'appel.", + "Call Declined": "Appel rejeté" } From 1214052e530b8946626d94e8e19d5d41504d2544 Mon Sep 17 00:00:00 2001 From: notramo Date: Mon, 26 Oct 2020 18:55:37 +0000 Subject: [PATCH 0130/4306] Translated using Weblate (Hungarian) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index ef3b1e6789..54ed44580f 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -330,7 +330,7 @@ "Start automatically after system login": "Rendszerindításkor automatikus elindítás", "Analytics": "Analitika", "Options": "Opciók", - "%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s személytelen analitikai adatokat gyűjt annak érdekében, hogy fejleszteni tudjuk az alkalmazást.", + "%(brand)s collects anonymous analytics to allow us to improve the application.": "A(z) %(brand)s névtelen analitikai adatokat gyűjt annak érdekében, hogy fejleszteni tudjuk az alkalmazást.", "Passphrases must match": "A jelmondatoknak meg kell egyezniük", "Passphrase must not be empty": "A jelmondat nem lehet üres", "Export room keys": "Szoba kulcsok mentése", @@ -339,7 +339,7 @@ "File to import": "Fájl betöltése", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "A kimentett fájl jelmondattal van védve. A kibontáshoz add meg a jelmondatot.", "You must join the room to see its files": "Ahhoz hogy lásd a fájlokat be kell lépned a szobába", - "Reject all %(invitedRooms)s invites": "Minden %(invitedRooms)s meghívó elutasítása", + "Reject all %(invitedRooms)s invites": "Mind a(z) %(invitedRooms)s meghívó elutasítása", "Failed to invite": "Meghívás sikertelen", "Failed to invite the following users to the %(roomName)s room:": "Az alábbi felhasználókat nem sikerült meghívni a(z) %(roomName)s szobába:", "Confirm Removal": "Törlés megerősítése", @@ -645,7 +645,7 @@ "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "A szűrő beállításához húzd a közösség avatarját a szűrő panel fölé a képernyő bal szélén. A szűrő panelen az avatarra kattintva bármikor leszűrheted azokat a szobákat és embereket akik a megadott közösséghez tartoznak.", "Key request sent.": "Kulcs kérés elküldve.", "Code": "Kód", - "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.": "Ha a GitHubon keresztül küldted be a hibát, a hibakeresési napló segíthet nekünk a javításban. A napló felhasználási adatokat tartalmaz mint a felhasználói neved, az általad meglátogatott szobák vagy csoportok azonosítóját vagy alternatív nevét és mások felhasználói nevét. De nem tartalmazzák az üzeneteket.", + "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.": "Ha a GitHubon keresztül küldted be a hibát, a hibakeresési napló segíthet nekünk a javításban. A napló felhasználási adatokat tartalmaz, mint például a felhasználói neved, az általad meglátogatott szobák vagy csoportok azonosítóját vagy alternatív nevét és mások felhasználói nevét. De nem tartalmazzák az üzeneteket.", "Submit debug logs": "Hibakeresési napló küldése", "Opens the Developer Tools dialog": "Megnyitja a fejlesztői eszközök ablakát", "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) az alábbi időpontban látta: %(dateTime)s", @@ -1210,7 +1210,7 @@ "User %(userId)s is already in the room": "%(userId)s felhasználó már a szobában van", "The user must be unbanned before they can be invited.": "A felhasználó kitiltását először vissza kell vonni mielőtt újra meghívható lesz.", "Upgrade to your own domain": "Frissíts a saját domain-re", - "Accept all %(invitedRooms)s invites": "Minden meghívást elfogad: %(invitedRooms)s", + "Accept all %(invitedRooms)s invites": "Mind a(z) %(invitedRooms)s meghívás elfogadása", "Change room avatar": "Szoba profilképének megváltoztatása", "Change room name": "Szoba nevének megváltoztatása", "Change main address for the room": "A szoba elsődleges címének megváltoztatása", @@ -1458,7 +1458,7 @@ "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "E-mail cím beállítása a fiók visszaállításához. E-mail cím, hogy ismerősök megtalálhassanak.", "Enter your custom homeserver URL What does this mean?": "Add meg a matrix szervered URL-jét Mit jelent ez?", "Enter your custom identity server URL What does this mean?": "Add meg az azonosítási szervered URL-jét Mit jelent ez?", - "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Használj azonosítási szervert e-mail címmel való meghíváshoz. Használd az alapértelmezett szervert (%(defaultIdentityServerName)s) vagy adj meg mást a Beállításokban.", + "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Használj azonosítási szervert az e-mail címmel való meghíváshoz. Használd az alapértelmezett szervert (%(defaultIdentityServerName)s) vagy adj meg mást a Beállításokban.", "Use an identity server to invite by email. Manage in Settings.": "Használj azonosítási szervert e-mail címmel való meghíváshoz. Megadása a Beállításokban.", "Enable room encryption": "Szoba titkosításának bekapcsolása", "Use an identity server": "Azonosítási kiszolgáló használata", @@ -1797,9 +1797,9 @@ "Channel: %(channelName)s": "Csatorna: %(channelName)s", "Show less": "Kevesebbet mutat", "Securely cache encrypted messages locally for them to appear in search results, using ": "A titkosított üzenetek kereséséhez azokat biztonságos módon helyileg kell tárolnod, felhasználva: ", - " to store messages from ": " üzenetek eltárolásához innen ", - "rooms.": "szobák.", - "Manage": "Kezel", + " to store messages from ": " üzenetek tárolásához ", + "rooms.": "szobából.", + "Manage": "Kezelés", "Securely cache encrypted messages locally for them to appear in search results.": "A titkosított üzenetek kereséséhez azokat biztonságos módon helyileg kell tárolnod.", "Enable": "Engedélyez", "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with search components added.": "A %(brand)sból a titkosított üzenetek biztonságos helyi tárolásához hiányzik néhány dolog. Ha kísérletezni szeretnél ezzel a lehetőséggel fordíts le egy saját %(brand)sot a kereső komponens hozzáadásával.", @@ -2375,7 +2375,7 @@ "Away": "Távol", "Are you sure you want to cancel entering passphrase?": "Biztos vagy benne, hogy megszakítod a jelmondat bevitelét?", "Enable advanced debugging for the room list": "Kibővített hibakeresés bekapcsolása a szoba listához", - "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.": "%(brand)s nem képes a web böngészőben futva biztonságosan elmenteni a titkosított üzeneteket helyben. Használd az Asztali %(brand)s ahhoz, hogy az üzenetekben való keresésekkor a titkosított üzenetek is megjelenhessenek.", + "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.": "A(z) %(brand)s nem képes a web böngészőben futva biztonságosan elmenteni a titkosított üzeneteket helyben. Használd az Asztali %(brand)s alkalmazást ahhoz, hogy az üzenetekben való keresésekkor a titkosított üzenetek is megjelenhessenek.", "You might have configured them in a client other than %(brand)s. You cannot tune them in %(brand)s but they still apply.": "Valószínűleg egy %(brand)s klienstől eltérő programmal konfiguráltad. %(brand)s kliensben nem tudod módosítani de attól még érvényesek.", "Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Add meg a rendszer által használt font nevét és %(brand)s megpróbálja majd azt használni.", "Add users and servers you want to ignore here. Use asterisks to have %(brand)s match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "A figyelmen kívül hagyandó felhasználókat és szervereket itt add meg. %(brand)s kliensben használj csillagot hogy a helyén minden karakterre illeszkedjen a kifejezés. Például: @bot:* figyelmen kívül fog hagyni minden „bot” nevű felhasználót bármely szerverről.", @@ -2484,7 +2484,7 @@ "Cross-signing is not set up.": "Eszközök közötti hitelesítés nincs beállítva.", "Backup version:": "Mentés verzió:", "Algorithm:": "Algoritmus:", - "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Ments el a titkosítási kulcsaidat a fiókadatokkal arra az esetre ha levesztenéd a hozzáférést a munkameneteidhez. A kulcsok egy egyedi visszaállítási kulccsal lesznek védve.", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Ments el a titkosítási kulcsaidat a fiókadatokkal arra az esetre ha elvesztenéd a hozzáférést a munkameneteidhez. A kulcsok egy egyedi visszaállítási kulccsal lesznek védve.", "Backup key stored:": "Mentési kulcs tár:", "Backup key cached:": "Mentési kulcs gyorsítótár:", "Secret storage:": "Biztonsági tároló:", @@ -2507,7 +2507,7 @@ "not found in storage": "a tárban nem található", "Widgets": "Kisalkalmazások", "Edit widgets, bridges & bots": "Kisalkalmazások, hidak és botok szerkesztése", - "Use the Desktop app to see all encrypted files": "Minden titkosított fájl eléréséhez használd az Asztali alkalmazást", + "Use the Desktop app to see all encrypted files": "Ahhoz, hogy elérd az összes titkosított fájlt, használd az Asztali alkalmazást", "Use the Desktop app to search encrypted messages": "A titkosított üzenetek kereséséhez használd az Asztali alkalmazást", "This version of %(brand)s does not support viewing some encrypted files": "%(brand)s ezen verziója nem minden titkosított fájl megjelenítését támogatja", "This version of %(brand)s does not support searching encrypted messages": "%(brand)s ezen verziója nem támogatja a keresést a titkosított üzenetekben", From fdb491d4e81eb46e13c5487c69cd541e9721991d Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Mon, 26 Oct 2020 18:27:35 +0000 Subject: [PATCH 0131/4306] Translated using Weblate (Swedish) Currently translated at 100.0% (2367 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sv/ --- src/i18n/strings/sv.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sv.json b/src/i18n/strings/sv.json index 4a74d88918..4cebdf4741 100644 --- a/src/i18n/strings/sv.json +++ b/src/i18n/strings/sv.json @@ -2471,5 +2471,16 @@ "(connection failed)": "(anslutning misslyckad)", "The call could not be established": "Samtalet kunde inte etableras", "The other party declined the call.": "Den andra parten avböjde samtalet.", - "Call Declined": "Samtal avböjt" + "Call Declined": "Samtal avböjt", + "Move right": "Flytta till höger", + "Move left": "Flytta till vänster", + "Revoke permissions": "Återkalla behörigheter", + "Data on this screen is shared with %(widgetDomain)s": "Data på den här skärmen delas med %(widgetDomain)s", + "Modal Widget": "Dialogruta", + "Unpin a widget to view it in this panel": "Avfäst en widget för att visa den i den här panelen", + "You can only pin up to %(count)s widgets|other": "Du kan bara fästa upp till %(count)s widgets", + "Show Widgets": "Visa widgets", + "Hide Widgets": "Dölj widgets", + "The call was answered on another device.": "Samtalet mottogs på en annan enhet.", + "Answered Elsewhere": "Mottaget någon annanstans" } From da00ab53adf06e9f6767e20dbcad670d47c9b542 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Mon, 26 Oct 2020 22:25:34 +0000 Subject: [PATCH 0132/4306] Translated using Weblate (Lithuanian) Currently translated at 81.2% (1923 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 330 +++++++++++++++++++++++++++++---------- 1 file changed, 250 insertions(+), 80 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index ad050d6e31..9adb7822ac 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1,7 +1,7 @@ { "This email address is already in use": "Šis el. pašto adresas jau naudojamas", "This phone number is already in use": "Šis telefono numeris jau naudojamas", - "Failed to verify email address: make sure you clicked the link in the email": "Nepavyko patikrinti el. pašto adreso: įsitikinkite, kad paspaudėte nuorodą el. laiške", + "Failed to verify email address: make sure you clicked the link in the email": "Nepavyko patvirtinti el. pašto adreso: įsitikinkite, kad paspaudėte nuorodą el. laiške", "The platform you're on": "Jūsų naudojama platforma", "The version of %(brand)s": "%(brand)s versija", "Your language of choice": "Jūsų pasirinkta kalba", @@ -28,7 +28,7 @@ "On": "Įjungta", "Changelog": "Keitinių žurnalas", "Waiting for response from server": "Laukiama atsakymo iš serverio", - "Failed to change password. Is your password correct?": "Nepavyko pakeisti slaptažodžio. Ar Jūsų slaptažodis teisingas?", + "Failed to change password. Is your password correct?": "Nepavyko pakeisti slaptažodžio. Ar jūsų slaptažodis teisingas?", "Uploaded on %(date)s by %(user)s": "Atnaujinta %(date)s vartotojo %(user)s", "OK": "Gerai", "Send Custom Event": "Siųsti pasirinktinį įvykį", @@ -46,26 +46,26 @@ "Downloading update...": "Atsiunčiamas atnaujinimas...", "Messages in one-to-one chats": "Žinutės privačiuose pokalbiuose", "Unavailable": "Neprieinamas", - "Error saving email notification preferences": "Klaida išsaugant pranešimų el. paštu nuostatas", + "Error saving email notification preferences": "Klaida saugojant el. pašto pranešimų nuostatas", "View Decrypted Source": "Peržiūrėti iššifruotą šaltinį", "Failed to update keywords": "Nepavyko atnaujinti raktažodžių", "Notifications on the following keywords follow rules which can’t be displayed here:": "Pranešimai šiems raktažodžiams yra uždrausti taisyklėmis:", "Please set a password!": "Prašau įrašykite slaptažodį!", "powered by Matrix": "veikia su Matrix", "You have successfully set a password!": "Jūs sėkmingai įrašėte slaptažodį!", - "Favourite": "Favoritai", + "Favourite": "Mėgstamas", "All Rooms": "Visi pokalbių kambariai", "Explore Room State": "Peržiūrėti kambario būseną", "Source URL": "Šaltinio URL adresas", - "Messages sent by bot": "Roboto siunčiamos žinutės", + "Messages sent by bot": "Boto siųstos žinutės", "Cancel": "Atšaukti", "Filter results": "Išfiltruoti rezultatus", "Members": "Nariai", "No update available.": "Nėra galimų atnaujinimų.", "Noisy": "Triukšmingas", - "Collecting app version information": "Renkama programėlės versijos informacija", + "Collecting app version information": "Renkama programos versijos informacija", "Keywords": "Raktažodžiai", - "Unpin Message": "Atsegti žinutę", + "Unpin Message": "Atsegti Žinutę", "Enable notifications for this account": "Įjungti pranešimus šiai paskyrai", "Remove": "Pašalinti", "Invite to this community": "Pakviesti į šią bendruomenę", @@ -81,7 +81,7 @@ "Unnamed room": "Kambarys be pavadinimo", "Dismiss": "Atsisakyti", "Explore Account Data": "Peržiūrėti paskyros duomenis", - "Remove from Directory": "Paš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.", @@ -99,7 +99,7 @@ "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 pamiršti kambario %(errCode)s", - "What's New": "Kas naujo", + "What's New": "Kas Naujo", "Wednesday": "Trečiadienis", "Send": "Siųsti", "Error": "Klaida", @@ -128,13 +128,13 @@ "Reject": "Atmesti", "Sorry, your browser is not able to run %(brand)s.": "Atleiskite, jūsų naršyklė negali paleisti %(brand)s.", "Quote": "Cituoti", - "Messages in group chats": "Žinutės grupės pokalbiuose", + "Messages in group chats": "Žinutės grupiniuose pokalbiuose", "Yesterday": "Vakar", "Error encountered (%(errorDetail)s).": "Susidurta su klaida (%(errorDetail)s).", "Low Priority": "Žemo prioriteto", "%(brand)s does not know how to join a room on this network": "%(brand)s nežino kaip prisijungti prie kambario šiame tinkle", "Set Password": "Nustatyti slaptažodį", - "An error occurred whilst saving your email notification preferences.": "Išsaugant pranešimų el. paštu nuostatas, įvyko klaida.", + "An error occurred whilst saving your email notification preferences.": "Saugojant jūsų el. pašto pranešimų nuostatas, įvyko klaida.", "Unable to join network": "Nepavyko prisijungti prie tinklo", "Register": "Registruotis", "Off": "Išjungta", @@ -143,7 +143,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 el. pašto pranešimus", + "Enable email notifications": "Įjungti pranešimus el. paštu", "Event Type": "Įvykio tipas", "No rooms to show": "Nėra kambarių rodymui", "Add rooms to this community": "Įtraukti kambarius į šią bendruomenę", @@ -151,7 +151,7 @@ "Failed to change settings": "Nepavyko pakeisti nustatymų", "Leave": "Išeiti", "View Community": "Peržiūrėti bendruomenes", - "Developer Tools": "Programuotojo įrankiai", + "Developer Tools": "Programuotojo Įrankiai", "Unhide Preview": "Rodyti paržiūrą", "Custom Server Options": "Pasirinktiniai Serverio Nustatymai", "Event Content": "Įvykio turinys", @@ -244,14 +244,14 @@ "Show timestamps in 12 hour format (e.g. 2:30pm)": "Rodyti laiko žymes 12 valandų formatu (pvz. 2:30pm)", "Always show message timestamps": "Visada rodyti žinučių laiko žymes", "Always show encryption icons": "Visada rodyti šifravimo piktogramas", - "Room Colour": "Kambario spalva", + "Room Colour": "Kambario Spalva", "Decline": "Atmesti", "Accept": "Priimti", "Incorrect verification code": "Neteisingas patvirtinimo kodas", "Submit": "Pateikti", "Phone": "Telefonas", "Add": "Pridėti", - "Failed to upload profile picture!": "Nepavyko įkelti profilio paveikslą!", + "Failed to upload profile picture!": "Nepavyko įkelti profilio paveikslėlio!", "Upload new:": "Įkelti naują:", "No display name": "Nėra rodomo vardo", "New passwords don't match": "Nauji slaptažodžiai nesutampa", @@ -260,7 +260,7 @@ "Do you want to set an email address?": "Ar norite nustatyti el. pašto adresą?", "Current password": "Dabartinis slaptažodis", "Password": "Slaptažodis", - "New Password": "Naujas slaptažodis", + "New Password": "Naujas Slaptažodis", "Failed to set display name": "Nepavyko nustatyti rodomo vardo", "Cannot add any more widgets": "Nepavyksta pridėti daugiau valdiklių", "Add a widget": "Pridėti valdiklį", @@ -347,7 +347,7 @@ "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?": "Panašu, kad jūs įkelinėjate failus, ar tikrai norite išeiti?", + "You seem to be uploading files, are you sure you want to quit?": "Panašu, kad jūs įkeliate 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 :(", @@ -419,7 +419,7 @@ "%(widgetName)s widget removed by %(senderName)s": "%(senderName)s pašalino %(widgetName)s valdiklį", "Failure to create room": "Nepavyko sukurti kambario", "Server may be unavailable, overloaded, or you hit a bug.": "Serveris gali būti neprieinamas, per daug apkrautas, arba susidūrėte su klaida.", - "Autoplay GIFs and videos": "Automatiškai atkurti GIF ir vaizdo įrašus", + "Autoplay GIFs and videos": "Automatiškai paleisti GIF ir vaizdo įrašus", "This event could not be displayed": "Nepavyko parodyti šio įvykio", "Kick": "Išmesti", "Kick this user?": "Išmesti šį naudotoją?", @@ -436,8 +436,8 @@ "%(duration)sm": "%(duration)s min", "%(duration)sh": "%(duration)s val", "%(duration)sd": "%(duration)s d", - "Seen by %(userName)s at %(dateTime)s": "%(userName)s matė ties %(dateTime)s", - "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) matė ties %(dateTime)s", + "Seen by %(userName)s at %(dateTime)s": "%(userName)s matė %(dateTime)s", + "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) matė %(dateTime)s", "Show these rooms to non-members on the community page and room list?": "Rodyti šiuos kambarius ne nariams bendruomenės puslapyje ir kambarių sąraše?", "Kicks user with given id": "Išmeta vartotoją su nurodytu id", "Bans user with given id": "Užblokuoja vartotoją su nurodytu id", @@ -456,8 +456,8 @@ "Incoming voice call from %(name)s": "Įeinantis balso skambutis nuo %(name)s", "Incoming video call from %(name)s": "Įeinantis vaizdo skambutis nuo %(name)s", "Incoming call from %(name)s": "Įeinantis skambutis nuo %(name)s", - "Change Password": "Keisti slaptažodį", - "Authentication": "Tapatybės nustatymas", + "Change Password": "Keisti Slaptažodį", + "Authentication": "Autentifikavimas", "The maximum permitted number of widgets have already been added to this room.": "Į šį kambarį jau yra pridėtas didžiausias leidžiamas valdiklių skaičius.", "Please select the destination room for this message": "Pasirinkite šiai žinutei paskirties kambarį", "Hangup": "Padėti ragelį", @@ -475,7 +475,7 @@ "%(targetName)s left the room.": "%(targetName)s išėjo iš kambario.", "%(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.", + "Please contact your homeserver administrator.": "Susisiekite su savo serverio administratoriumi.", "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 kaip numatytasias šiame kambaryje esantiems dalyviams", @@ -548,7 +548,7 @@ "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": "Įjungti valdiklių ekrano kopijas palaikomuose valdikliuose", - "Export E2E room keys": "Eksportuoti E2E kambarių raktus", + "Export E2E room keys": "Eksportuoti E2E (visapusio šifravimo) kambarių raktus", "Last seen": "Paskutinį kartą matytas", "Unignore": "Nebeignoruoti", "and %(count)s others...|other": "ir %(count)s kitų...", @@ -624,8 +624,8 @@ "Avoid repeated words and characters": "Venkite pasikartojančių žodžių ir simbolių", "Use a few words, avoid common phrases": "Naudokite keletą žodžių, venkite dažnai naudojamų frazių", "No need for symbols, digits, or uppercase letters": "Nereikia simbolių, skaitmenų ar didžiųjų raidžių", - "Encrypted messages in group chats": "Šifruotos žinutės grupės pokalbiuose", - "Delete Backup": "Ištrinti atsarginę kopiją", + "Encrypted messages in group chats": "Šifruotos žinutės grupiniuose pokalbiuose", + "Delete Backup": "Ištrinti Atsarginę Kopiją", "Backup version: ": "Atsarginės kopijos versija: ", "Algorithm: ": "Algoritmas: ", "Don't ask again": "Daugiau nebeklausti", @@ -638,7 +638,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": "Deaktyvuoti paskyrą", + "Deactivate Account": "Deaktyvuoti Paskyrą", "Incompatible local cache": "Nesuderinamas vietinis podėlis", "Updating %(brand)s": "Atnaujinama %(brand)s", "This doesn't appear to be a valid email address": "Tai nepanašu į teisingą el. pašto adresą", @@ -785,7 +785,7 @@ "Use a longer keyboard pattern with more turns": "Naudokite ilgesnį klaviatūros modelį su daugiau vijų", "There was an error joining the room": "Prisijungiant prie kambario įvyko klaida", "Failed to join room": "Prisijungti prie kambario nepavyko", - "Message Pinning": "Žinutės prisegimas", + "Message Pinning": "Žinutės Prisegimas", "Custom user status messages": "Pasirinktinės vartotojo būsenos žinutės", "Group & filter rooms by custom tags (refresh to apply changes)": "Grupuoti ir filtruoti kambarius pagal pasirinktines žymas (atnaujinkite, kad pritaikytumėte pakeitimus)", "Render simple counters in room header": "Užkrauti paprastus skaitiklius kambario antraštėje", @@ -804,7 +804,7 @@ " invited you": " jus pakvietė", "You're previewing %(roomName)s. Want to join it?": "Jūs peržiūrite %(roomName)s. Norite prie jo prisijungti?", "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s negali būti peržiūrėtas. Ar jūs norite prie jo prisijungti?", - "This room doesn't exist. Are you sure you're at the right place?": "Šis kambarys neegzistuoja. Ar jūs tikras, kad esate tinkamoje vietoje?", + "This room doesn't exist. Are you sure you're at the right place?": "Šis kambarys neegzistuoja. Ar jūs tikri, kad esate tinkamoje vietoje?", "Create a public room": "Sukurti viešą kambarį", "Make this room public": "Padaryti šį kambarį viešu", "Room Settings - %(roomName)s": "Kambario nustatymai - %(roomName)s", @@ -941,7 +941,7 @@ "Show read receipts sent by other users": "Rodyti kitų vartotojų siųstus perskaitymo kvitus", "Order rooms by name": "Rūšiuoti kambarius pagal pavadinimą", "The other party cancelled the verification.": "Kita šalis atšaukė patvirtinimą.", - "Public Name": "Viešas pavadinimas", + "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 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ą", @@ -1016,7 +1016,7 @@ "Send as message": "Siųsti kaip žinutę", "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.", + "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 šį vartotoją jo vartotojo profilyje.", "Confirm Removal": "Patvirtinkite pašalinimą", "Manually export keys": "Eksportuoti raktus rankiniu būdu", "Send a Direct Message": "Siųsti tiesioginę žinutę", @@ -1024,12 +1024,12 @@ "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.": "Rekomenduojame, prieš atsijungiant, iš tapatybės 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.": "Prieš atsijungiant rekomenduojame iš tapatybės serverio pašalinti savo el. pašto adresus ir telefono numerius.", "Email addresses": "El. pašto adresai", - "Account management": "Paskyros valdymas", + "Account management": "Paskyros tvarkymas", "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ą.", + "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 savo adresą. Sekite ten pateiktas instrukcijas ir tada paspauskite žemiau esantį mygtuką.", "Email Address": "El. pašto adresas", "Enter your custom homeserver URL What does this mean?": "Įveskite pasirinktinio serverio URL Ką tai reiškia?", "Homeserver URL": "Serverio URL", @@ -1076,8 +1076,8 @@ "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": "Patikrinti", + "To be secure, do this in person or use a trusted way to communicate.": "Norėdami užtikrinti saugumą, darykite tai asmeniškai arba naudokite patikimą komunikacijos būdą.", + "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", @@ -1085,9 +1085,9 @@ "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", + "Restore from Backup": "Atkurti iš Atsarginės Kopijos", "Flair": "Ženkliukai", - "Access Token:": "Prieigos talonas:", + "Access Token:": "Prieigos Talonas:", "Preferences": "Nuostatos", "Cryptography": "Kriptografija", "Security & Privacy": "Saugumas ir Privatumas", @@ -1110,7 +1110,7 @@ "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ą", + "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", @@ -1135,7 +1135,7 @@ "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ą", "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ų:", + "Use this session to verify your new one, granting it access to encrypted messages:": "Panaudoti šį seansą naujo patvirtinimui, suteikiant 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 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.", @@ -1153,7 +1153,7 @@ "Verify this login": "Patvirtinkite šį 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.": "Identifikuokite save patvirtindami šį prisijungimą viename iš kitų jūsų seansų ir suteikdami jam prieigą prie šifruotų žinučių.", + "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Identifikuokite save, patvirtindami šį prisijungimą viename iš kitų jūsų seansų, ir suteikdami jam prieigą prie šifruotų žinučių.", "This requires the latest %(brand)s on your other devices:": "Tam reikia naujausios %(brand)s versijos kituose jūsų įrenginiuose:", "or another cross-signing capable Matrix client": "arba kito kryžminį pasirašymą palaikančio Matrix kliento", "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.", @@ -1165,24 +1165,24 @@ "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ą.", "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", + "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", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "patikrinti 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 ": "susisiekti 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)", + "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ų tvarkytuvą (%(serverName)s) botų, valdiklių ir lipdukų valdymui.", - "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Naudokite integracijų tvarkytuvą botų, valdiklių ir lipdukų valdymui.", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Naudokite Integracijų Tvarkytuvą (%(serverName)s) botų, valdiklių ir lipdukų pakuočių tvarkymui.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Naudokite Integracijų Tvarkytuvą botų, valdiklių ir lipdukų pakuočių tvarkymui.", "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.", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integracijų Tvarkytuvai 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!", @@ -1196,13 +1196,13 @@ "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.", + "Unable to verify phone number.": "Nepavyko 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.": "Įjunkite 'Valdyti integracijas' nustatymuose, kad tai atliktumėte.", + "Enable 'Manage Integrations' in Settings to do this.": "Įjunkite 'Valdyti integracijas' Nustatymuose, kad tai atliktumėte.", "Your %(brand)s doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Jūsų %(brand)s neleidžia jums naudoti integracijų tvarkytuvo 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į", @@ -1213,11 +1213,11 @@ "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Ar jūs naudojate %(brand)s į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 Emoji suggestions while typing": "Įjungti jaustukų pasiūlymus rašant", + "Enable Emoji suggestions while typing": "Įjungti Jaustukų pasiūlymus rašant", "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Rodyti priminimą įjungti saugių ž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 jaustukus pokalbiuose", - "Enable Community Filter Panel": "Įjungti bendruomenės filtrų skydelį", + "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ą.", @@ -1267,7 +1267,7 @@ "Glasses": "Akiniai", "Spanner": "Veržliaraktis", "Santa": "Santa", - "Thumbs up": "Liuks", + "Thumbs up": "Liuksas", "Umbrella": "Skėtis", "Hourglass": "Smėlio laikrodis", "Clock": "Laikrodis", @@ -1317,7 +1317,7 @@ "Backup has an invalid signature from unverified session ": "Atsarginė kopija turi negaliojantį nepatvirtinto seanso parašą", "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ą", + "wait and try again later": "palaukti ir bandyti 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 serverio 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", @@ -1346,8 +1346,8 @@ "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", - "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.", + "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ą. Įrenginio pažymėjimas patikimu jums ir kitiems vartotojams suteikia papildomos ramybės naudojant 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", "Session verified": "Seansas patvirtintas", @@ -1369,7 +1369,7 @@ "Show avatar changes": "Rodyti pseudoportretų pakeitimus", "Show avatars in user and room mentions": "Rodyti pseudoportretus vartotojo ir kambario paminėjimuose", "Send typing notifications": "Siųsti spausdinimo pranešimus", - "Automatically replace plain text Emoji": "Automatiškai pakeisti paprasto teksto jaustukus", + "Automatically replace plain text Emoji": "Automatiškai pakeisti paprasto teksto Jaustukus", "Mirror local video feed": "Atkartoti lokalų video tiekimą", "Allow Peer-to-Peer for 1:1 calls": "Leisti tiesioginį \"Peer-to-Peer\" sujungimą 1:1 skambučiams", "Prompt before sending invites to potentially invalid matrix IDs": "Klausti prieš siunčiant pakvietimus galimai netinkamiems matrix ID", @@ -1382,7 +1382,7 @@ "When rooms are upgraded": "Kai atnaujinami kambariai", "My Ban List": "Mano Draudimų Sąrašas", "This is your list of users/servers you have blocked - don't leave the room!": "Tai yra jūsų užblokuotų vartotojų/serverių sąrašas - neišeikite iš kambario!", - "Verify this user by confirming the following emoji appear on their screen.": "Patvirtinkite šį vartotoją, įsitikindami, kad šie jaustukai rodomi jo ekrane.", + "Verify this user by confirming the following emoji appear on their screen.": "Patvirtinkite šį vartotoją, įsitikindami, kad jo ekrane rodomas toliau esantis jaustukas.", "⚠ These settings are meant for advanced users.": "⚠ Šie nustatymai yra skirti pažengusiems vartotojams.", "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Jūsų asmeniniame draudimų sąraše yra visi vartotojai/serveriai, iš kurių jūs asmeniškai nenorite matyti pranešimų. Po pirmojo jūsų vartotojo/serverio ignoravimo, jūsų kambarių sąraše pasirodys naujas kambarys pavadinimu 'Mano Draudimų Sąrašas' - likite šiame kambaryje, kad draudimų sąrašas veiktų.", "Room list": "Kambarių sąrašas", @@ -1392,7 +1392,7 @@ "Read Marker off-screen lifetime (ms)": "Skaitymo žymeklio ne ekraninis veikimo laikas (ms)", "You can use /help to list available commands. Did you mean to send this as a message?": "Jūs galite naudoti /help, kad pamatytumėte galimų komandų sąrašą. Ar norėjote siųsti tai kaip žinutę?", "Room avatar": "Kambario pseudoportretas", - "Verify by comparing unique emoji.": "Patvirtinkite palygindami unikalius jaustukus.", + "Verify by comparing unique emoji.": "Patvirtinti palyginant unikalius jaustukus.", "Verify by emoji": "Patvirtinti naudojant jaustukus", "Compare emoji": "Palyginkite jaustukus", "Show image": "Rodyti vaizdą", @@ -1440,7 +1440,7 @@ "Custom (%(level)s)": "Pasirinktinis (%(level)s)", "Ask this user to verify their session, or manually verify it below.": "Paprašykite šio vartotojo patvirtinti savo seansą, arba patvirtinkite jį rankiniu būdu žemiau.", "Encryption upgrade available": "Galimas šifravimo atnaujinimas", - "Verify this user by confirming the following number appears on their screen.": "Patvirtinkite šį vartotoją įsitikindami, kad jo ekrane rodomas toliau esantis skaičius.", + "Verify this user by confirming the following number appears on their screen.": "Patvirtinkite šį vartotoją, įsitikindami, kad jo ekrane rodomas toliau esantis skaičius.", "Backup has a signature from unknown user with ID %(deviceId)s": "Atsarginė kopija turi nežinomo vartotojo parašą su ID %(deviceId)s", "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Tvarkykite savo seansų pavadinimus ir iš jų atsijunkite žemiau, arba patvirtinkite juos savo Vartotojo Profilyje.", "Please enter verification code sent via text.": "Įveskite patvirtinimo kodą išsiųstą teksto žinute.", @@ -1455,7 +1455,7 @@ "You sent a verification request": "Jūs išsiuntėte patvirtinimo užklausą", "Widgets do not use message encryption.": "Valdikliai nenaudoja žinučių šifravimo.", "Continue With Encryption Disabled": "Tęsti išjungus šifravimą", - "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Patvirtinkite šį vartotoją, kad pažymėtumėte jį kaip patikimą. Pažymint vartotojus kaip patikimus suteikia papildomos ramybės naudojant visapusiškai užšifruotas žinutes.", + "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Patvirtinkite šį vartotoją, kad pažymėtumėte jį kaip patikimą. Vartotojų pažymėjimas patikimais suteikia jums papildomos ramybės naudojant visapusiškai užšifruotas žinutes.", "Waiting for partner to confirm...": "Laukiama kol partneris patvirtins...", "Start a conversation with someone using their name, username (like ) or email address.": "Pradėkite pokalbį su kuo nors naudodami jų vardą, vartotojo vardą (kaip ) arba el. pašto adresą.", "Invite someone using their name, username (like ), email address or share this room.": "Pakvieskite ką nors naudodami jų vardą, vartotojo vardą (kaip ), el. pašto adresą arba bendrinkite šį kambarį.", @@ -1476,15 +1476,15 @@ "Session backup key:": "Seanso atsarginės kopijos raktas:", "Unable to load key backup status": "Nepavyko įkelti atsarginės raktų kopijos būklės", "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Prieš atsijungdami prijunkite šį seansą prie atsarginės raktų kopijos, kad neprarastumėte raktų, kurie gali būti tik šiame seanse.", - "Connect this session to Key Backup": "Prijungtii šį seansą prie atsarginės raktų kopijos", + "Connect this session to Key Backup": "Prijungti šį seansą prie Atsarginės Raktų Kopijos", "Backup key stored: ": "Atsarginės kopijos raktas saugomas: ", "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Tam, kad galėtumėte rasti ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, jūs šiuo metu naudojate tapatybės serverį. Jį pakeisti galite žemiau.", - "Identity Server": "Tapatybės serveris", + "Identity Server": "Tapatybės Serveris", "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Šiuo metu jūs nenaudojate tapatybės serverio. Tam, kad galėtumėte rasti ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, pridėkite jį žemiau.", "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.": "Atsijungimas nuo tapatybės serverio reikš, kad jūs nebebūsite randamas kitų vartotojų ir jūs nebegalėsite pakviesti kitų, naudodami jų el. paštą arba telefoną.", "Appearance": "Išvaizda", "Deactivate account": "Deaktyvuoti paskyrą", - "Identity Server is": "Tapatybės serveris yra", + "Identity Server is": "Tapatybės Serveris yra", "Timeline": "Laiko juosta", "Key backup": "Atsarginė raktų kopija", "Where you’re logged in": "Kur esate prisijungę", @@ -1502,7 +1502,7 @@ "No identity server is configured: add one in server settings to reset your password.": "Nesukonfigūruotas joks tapatybės serveris: pridėkite jį serverio nustatymuose, kad iš naujo nustatytumėte slaptažodį.", "Identity server URL does not appear to be a valid identity server": "Tapatybės serverio URL neatrodo kaip tinkamas tapatybės serveris", "Scroll up/down in the timeline": "Slinkti aukštyn/žemyn laiko juostoje", - "Show developer tools": "Rodyti vystytojo įrankius", + "Show developer tools": "Rodyti programuotojo įrankius", "Low bandwidth mode": "Žemo duomenų pralaidumo režimas", "Send read receipts for messages (requires compatible homeserver to disable)": "Siųsti žinučių perskaitymo kvitus (norint išjungti reikalingas suderinamas serveris)", "How fast should messages be downloaded.": "Kaip greitai žinutės turi būti parsiųstos.", @@ -1522,7 +1522,7 @@ "exists": "yra", "This session is backing up your keys. ": "Šis seansas kuria atsargines jūsų raktų kopijas. ", "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Šis seansas nekuria atsarginių raktų kopijų, bet jūs jau turite atsarginę kopiją iš kurios galite atkurti ir pridėti.", - "All keys backed up": "Atsarginė kopija sukurta visiems raktams", + "All keys backed up": "Atsarginės kopijos sukurtos visiems raktams", "Backup has a valid signature from this user": "Atsarginė kopija turi galiojantį šio vartotojo parašą", "Backup has a invalid signature from this user": "Atsarginė kopija turi negaliojantį šio vartotojo parašą", "Backup has a signature from unknown session with ID %(deviceId)s": "Atsarginė kopija turi nežinomo seanso parašą su ID %(deviceId)s", @@ -1544,8 +1544,8 @@ "Your homeserver does not support session management.": "Jūsų serveris nepalaiko seansų tvarkymo.", "Your homeserver has exceeded its user limit.": "Jūsų serveris pasiekė savo vartotojų limitą.", "Your homeserver has exceeded one of its resource limits.": "Jūsų serveris pasiekė vieną iš savo resursų limitų.", - "Never send encrypted messages to unverified sessions from this session": "Niekada iš šio seanso nesiųsti šifruotų žinučių nepatvirtintiems seansams", - "Never send encrypted messages to unverified sessions in this room from this session": "Niekada iš šio seanso nesiųsti šifruotų žinučių šiame kambaryje nepatvirtintiems seansams", + "Never send encrypted messages to unverified sessions from this session": "Niekada nesiųsti šifruotų žinučių nepatvirtintiems seansams iš šio seanso", + "Never send encrypted messages to unverified sessions in this room from this session": "Niekada nesiųsti šifruotų žinučių nepatvirtintiems seansams šiame kambaryje iš šio seanso", "Unable to load session list": "Neįmanoma įkelti seansų sąrašo", "Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Patvirtinkite šių seansų ištrinimą, naudodamiesi Vienu Prisijungimu, kad įrodytumėte savo tapatybę.", "Confirm deleting these sessions by using Single Sign On to prove your identity.|one": "Patvirtinkite šio seanso ištrinimą, naudodamiesi Vienu Prisijungimu, kad įrodytumėte savo tapatybę.", @@ -1563,10 +1563,10 @@ "Clear cache and reload": "Išvalyti podėlį ir perkrauti", "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.": "Norėdami pranešti apie su Matrix susijusią saugos problemą, perskaitykite Matrix.org Saugumo Atskleidimo Poliiką.", "FAQ": "DUK", - "Keyboard Shortcuts": "Spartieji klavišai", + "Keyboard Shortcuts": "Spartieji Klavišai", "Versions": "Versijos", "Homeserver is": "Serveris yra", - "Import E2E room keys": "Importuoti E2E kambarių raktus", + "Import E2E room keys": "Importuoti E2E (visapusio šifravimo) kambarių raktus", "Session ID:": "Seanso ID:", "Session key:": "Seanso raktas:", "%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s renka anoniminę analizę, kad galėtume patobulinti programą.", @@ -1602,7 +1602,7 @@ "New Recovery Method": "Naujas atgavimo metodas", "A new recovery passphrase and key for Secure Messages have been detected.": "Buvo aptikta nauja atgavimo slaptafrazė ir saugių žinučių raktas.", "This session is encrypting history using the new recovery method.": "Šis seansas šifruoja istoriją naudodamas naują atgavimo metodą.", - "Recovery Method Removed": "Atgavimo metodas pašalintas", + "Recovery Method Removed": "Atgavimo Metodas Pašalintas", "This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Ši seansas aptiko, kad buvo pašalinta jūsų atgavimo slaptafrazė ir saugių žinučių raktas.", "If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Jei tai padarėte netyčia, šiame seanse galite nustatyti saugias žinutes, kurios pakartotinai užšifruos šio seanso žinučių istoriją nauju atgavimo metodu.", "Toggle Bold": "Perjungti paryškinimą", @@ -1667,9 +1667,9 @@ "Unencrypted": "Neužšifruota", "Encrypted by an unverified session": "Užšifruota nepatvirtinto seanso", "Encrypted": "Užšifruota", - "Securely cache encrypted messages locally for them to appear in search results.": "Šifruotus pranešimus saugiai talpinkite lokalioje talpykloje, kad jie būtų rodomi paieškos rezultatuose.", - "Securely cache encrypted messages locally for them to appear in search results, using ": "Šifruotus pranešimus saugiai talpinkite lokalioje talpykloje, kad jie būtų rodomi paieškos rezultatuose, naudojant ", - "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Saugios žinutės su šiuo vartotoju yra visapusiškai užšifruotos ir jų negali perskaityti trečiosios šalys.", + "Securely cache encrypted messages locally for them to appear in search results.": "Šifruotas žinutes saugiai talpinkite lokaliai, kad jos būtų rodomos paieškos rezultatuose.", + "Securely cache encrypted messages locally for them to appear in search results, using ": "Šifruotas žinutes saugiai talpinkite lokaliai, kad jos būtų rodomos paieškos rezultatuose, naudojant ", + "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Saugios žinutės su šiuo vartotoju yra visapusiškai užšifruotos ir negali būti perskaitytos trečiųjų šalių.", "Safeguard against losing access to encrypted messages & data": "Apsisaugokite nuo prieigos prie šifruotų žinučių ir duomenų praradimo", "Main address": "Pagrindinis adresas", "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Atnaujinant pagrindinį kambario adresą įvyko klaida. Gali būti, kad serveris to neleidžia arba įvyko laikina klaida.", @@ -1722,7 +1722,7 @@ "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Privačius kambarius rasti ir prie jų prisijungti galima tik su pakvietimu. Viešus kambarius rasti ir prie jų prisijungti gali visi šioje bendruomenėje.", "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone.": "Privačius kambarius rasti ir prie jų prisijungti galima tik su pakvietimu. Viešus kambarius rasti ir prie jų prisijungti gali visi.", "Join": "Prisijungti", - "Join the conference from the room information card on the right": "Prisijunkite prie konferencijos iš kambario informacijos kortelės dešinėje", + "Join the conference from the room information card on the right": "Prisijunkite prie konferencijos kambario informacijos kortelėje dešinėje", "Join the conference at the top of this room": "Prisijunkite prie konferencijos šio kambario viršuje", "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.": "Paskelbtus adresus gali naudoti bet kas, bet kuriame serveryje, kad prisijungtų prie jūsų kambario. Tam, kad paskelbtumėte adresą, visų pirma jis turi būti nustatytas kaip lokalus adresas.", "Join the discussion": "Prisijungti prie diskusijos", @@ -1799,8 +1799,8 @@ "Please supply a widget URL or embed code": "Pateikite valdiklio URL arba įterpimo kodą", "Could not find user in room": "Vartotojo rasti kambaryje nepavyko", "Command failed": "Komanda nepavyko", - "Unrecognised room address:": "", - "You signed in to a new session without verifying it:": "Jūs prisijungėte prie naujo seanso jo nepatikrinę:", + "Unrecognised room address:": "Neatpažintas kambario adresas:", + "You signed in to a new session without verifying it:": "Jūs prisijungėte prie naujo seanso, jo nepatvirtinę:", "You can also set up Secure Backup & manage your keys in Settings.": "Jūs taip pat galite nustatyti Saugią Atsarginę Kopiją ir tvarkyti savo raktus Nustatymuose.", "Secure Backup": "Saugi Atsarginė Kopija", "Set up Secure Backup": "Nustatyti Saugią Atsarginę Kopiją", @@ -1837,5 +1837,175 @@ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s atnaujino draudimo taisyklę, sutampančią su %(glob)s dėl %(reason)s", "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią serverius, sutampančius su %(glob)s dėl %(reason)s", "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią kambarius, sutampančius su %(glob)s dėl %(reason)s", - "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią vartotojus, sutampančius su %(glob)s dėl %(reason)s" + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s atnaujino taisyklę, draudžiančią vartotojus, sutampančius su %(glob)s dėl %(reason)s", + "Emoji picker": "Jaustukų rinkėjas", + "Failed to remove tag %(tagName)s from room": "Nepavyko pašalinti žymos %(tagName)s iš kambario", + "The user '%(displayName)s' could not be removed from the summary.": "Vartotojas '%(displayName)s' negali būti pašalintas iš santraukos.", + "Failed to remove a user from the summary of %(groupId)s": "Nepavyko pašalinti vartotojo iš %(groupId)s santraukos", + "The room '%(roomName)s' could not be removed from the summary.": "Kambarys '%(roomName)s' negali būti pašalintas iš santraukos.", + "Failed to remove the room from the summary of %(groupId)s": "Nepavyko pašalinti kambario iš %(groupId)s santraukos", + "Your browser likely removed this data when running low on disk space.": "Jūsų naršyklė greičiausiai pašalino šiuos duomenis pritrūkus vietos diske.", + "Remove server": "Pašalinti serverį", + "Remove %(count)s messages|one": "Pašalinti 1 žinutę", + "Remove %(count)s messages|other": "Pašalinti %(count)s žinutes(-ų)", + "Remove %(phone)s?": "Pašalinti %(phone)s?", + "Remove %(email)s?": "Pašalinti %(email)s?", + "Remove messages sent by others": "Pašalinti kitų siųstas žinutes", + "Remove for everyone": "Pašalinti visiems", + "Popout widget": "Iššokti valdiklį", + "Unpin a widget to view it in this panel": "Atsekite valdiklį, kad galėtumėte jį peržiūrėti šiame skydelyje", + "Unpin": "Atsegti", + "Video conference started by %(senderName)s": "%(senderName)s pradėjo video konferenciją", + "Video conference updated by %(senderName)s": "%(senderName)s atnaujino video konferenciją", + "Video conference ended by %(senderName)s": "%(senderName)s užbaigė video konferenciją", + "Start automatically after system login": "Pradėti automatiškai prisijungus prie sistemos", + "Subscribe": "Prenumeruoti", + "Room ID or address of ban list": "Kambario ID arba draudimų sąrašo adresas", + "If this isn't what you want, please use a different tool to ignore users.": "Jei tai nėra ko jūs norite, naudokite kitą įrankį vartotojams ignoruoti.", + "Subscribed lists": "Prenumeruojami sąrašai", + "eg: @bot:* or example.org": "pvz.: @botas:* arba pavyzdys.org", + "Server or user ID to ignore": "Norimo ignoruoti serverio arba vartotojo ID", + "Personal ban list": "Asmeninis draudimų sąrašas", + "Add users and servers you want to ignore here. Use asterisks to have %(brand)s match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Čia pridėkite vartotojus ir serverius, kuriuos norite ignoruoti. Naudokite žvaigždutes, kad %(brand)s atitiktų bet kokius simbolius. Pavyzdžiui, @botas:* ignoruotų visus vartotojus, turinčius vardą 'botas' bet kuriame serveryje.", + "Ignored users": "Ignoruojami vartotojai", + "View rules": "Peržiūrėti taisykles", + "Unsubscribe": "Atsisakyti prenumeratos", + "You have not ignored anyone.": "Jūs nieko neignoruojate.", + "User rules": "Vartotojo taisyklės", + "Server rules": "Serverio taisyklės", + "Ban list rules - %(roomName)s": "Draudimo sąrašo taisyklės - %(roomName)s", + "None": "Nė vienas", + "Please try again or view your console for hints.": "Bandykite dar kartą arba peržiūrėkite konsolę, kad rastumėte užuominų.", + "Error unsubscribing from list": "Klaida atsisakant sąrašo prenumeratos", + "Error removing ignored user/server": "Klaida pašalinant ignoruojamą vartotoją/serverį", + "Error subscribing to list": "Klaida užsiprenumeruojant sąrašą", + "Something went wrong. Please try again or view your console for hints.": "Kažkas ne taip. Bandykite dar kartą arba peržiūrėkite konsolę, kad rastumėte užuominų.", + "Error adding ignored user/server": "Klaida pridedant ignoruojamą vartotoją/serverį", + "Ignored/Blocked": "Ignoruojami/Blokuojami", + "Customise your experience with experimental labs features. Learn more.": "Tinkinkite savo patirtį su eksperimentinėmis laboratorijų funkcijomis. Sužinoti daugiau.", + "Labs": "Laboratorijos", + "Legal": "Teisiniai", + "Appearance Settings only affect this %(brand)s session.": "Išvaizdos Nustatymai įtakoja tik šį %(brand)s seansą.", + "Customise your appearance": "Tinkinti savo išvaizdą", + "Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Nustatykite sistemoje įdiegto šrifto pavadinimą ir %(brand)s bandys jį naudoti.", + "Modern": "Modernus", + "Compact": "Kompaktiškas", + "Message layout": "Žinutės išdėstymas", + "Use between %(min)s pt and %(max)s pt": "Naudokite dydį tarp %(min)s pt ir %(max)s pt", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Pasirinktinis šrifto dydis gali būti tik tarp %(min)s pt ir %(max)s pt", + "Size must be a number": "Dydis turi būti skaičius", + "Hey you. You're the best!": "Labukas. Tu geriausias(-a)!", + "New version available. Update now.": "Galima nauja versija. Atnaujinti dabar.", + "You should:": "Jūs turėtumėte:", + "Checking server": "Tikrinamas serveris", + "not ready": "neparuošta", + "ready": "paruošta", + "Secret storage:": "Slapta saugykla:", + "Backup key cached:": "Atsarginis raktas išsaugotas talpykloje:", + "not stored": "nesaugomas", + "Backup key stored:": "Atsarginis raktas saugomas:", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key.": "Sukurkite šifravimo raktų atsarginę kopiją su savo paskyros duomenimis, jei prarastumėte prieigą prie savo seansų. Jūsų raktai bus apsaugoti unikaliu Atkūrimo Raktu.", + "Algorithm:": "Algoritmas:", + "Backup version:": "Atsarginės kopijos versija:", + "Backup is not signed by any of your sessions": "Atsarginė kopija nepasirašyta nė vieno jūsų seanso", + "Backing up %(sessionsRemaining)s keys...": "Daromos atsarginės %(sessionsRemaining)s raktų kopijos...", + "Profile picture": "Profilio paveikslėlis", + "The operation could not be completed": "Nepavyko užbaigti operacijos", + "Failed to save your profile": "Nepavyko išsaugoti jūsų profilio", + "You might have configured them in a client other than %(brand)s. You cannot tune them in %(brand)s but they still apply.": "Galbūt juos sukonfigūravote ne %(brand)s kliente, o kitame. Jūs negalite derinti jų %(brand)s, bet jie vis tiek taikomi.", + "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)": "Prašau pamiršti visas mano siųstas žinutes, kai mano paskyra bus deaktyvuota (Įspėjimas: tai neleis būsimiems vartotojams pamatyti pilnų pokalbių vaizdo)", + "Forget Room": "Pamiršti Kambarį", + "Forget this room": "Pamiršti šį kambarį", + "You are a member of this community": "Jūs esate šios bendruomenės narys", + "You are an administrator of this community": "Jūs esate šios bendruomenės administratorius", + "This homeserver would like to make sure you are not a robot.": "Šis serveris norėtų įsitikinti, kad jūs nesate robotas.", + "Your area is experiencing difficulties connecting to the internet.": "Jūsų vietovėje kyla sunkumų prisijungiant prie interneto.", + "Your server isn't responding to some of your requests. Below are some of the most likely reasons.": "Jūsų serveris neatsako į kai kurias jūsų užklausas. Žemiau pateikiamos kelios labiausiai tikėtinos priežastys.", + "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.": "Žinučių matomumas Matrix yra panašus į el. paštą. Jūsų žinučių užmiršimas reiškia, kad žinutėmis, kurias jūs išsiuntėte, nebus dalijamasi su jokiais naujais arba neregistruotais vartotojais, bet registruoti vartotojai, kurie jau turi prieigą prie šių žinučių, vis tiek turės prieigą prie savo kopijų.", + "Your messages are not secure": "Jūsų žinutės nėra saugios", + "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.": "Raktų bendrinimo užklausos jūsų kitiems seansams yra siunčiamos automatiškai. Jei jūs atmetėte arba nutraukėte raktų bendrinimo užklausą kitame savo seanse, spauskite čia, kad vėl paprašytumėte šio seanso raktų.", + "Your key share request has been sent - please check your other sessions for key share requests.": "Jūsų raktų bendrinimo užklausa buvo išsiųsta - patikrinkite, ar kituose jūsų seansuose nėra raktų bendrinimo užklausų.", + "You are currently subscribed to:": "Šiuo metu esate užsiprenumeravę:", + "You are not subscribed to any lists": "Nesate užsiprenumeravę jokių sąrašų", + "You are currently ignoring:": "Šiuo metu ignoruojate:", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Ar tikrai? Jūs prarasite savo šifruotas žinutes, jei jūsų raktams nebus tinkamai sukurtos atsarginės kopijos.", + "Your keys are not being backed up from this session.": "Jūsų raktams nėra daromos atsarginės kopijos iš šio seanso.", + "Community IDs may only contain characters a-z, 0-9, or '=_-./'": "Bendruomenės ID gali turėti tik šiuos simbolius a-z, 0-9, or '=_-./'", + "A-Z": "A-Ž", + "Activity": "Aktyvumą", + "Sort by": "Rūšiuoti pagal", + "List options": "Sąrašo parinktys", + "Notification Autocomplete": "Pranešimo Automatinis Užbaigimas", + "Room Notification": "Kambario Pranešimas", + "You have %(count)s unread notifications in a prior version of this room.|one": "Jūs turite %(count)s neperskaitytą pranešimą ankstesnėje šio kambario versijoje.", + "You have %(count)s unread notifications in a prior version of this room.|other": "Jūs turite %(count)s neperskaitytus(-ų) pranešimus(-ų) ankstesnėje šio kambario versijoje.", + "You have no visible notifications in this room.": "Šiame kambaryje neturite matomų pranešimų.", + "Notification options": "Pranešimų parinktys", + "Leave Room": "Išeiti Iš Kambario", + "Favourited": "Mėgstamas", + "Room options": "Kambario parinktys", + "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.": "%(brand)s negali saugiai talpinti šifruotų žinučių lokaliai, kai veikia interneto naršyklėje. Naudokite %(brand)s Desktop (darbastalio versija), kad šifruotos žinutės būtų rodomos paieškos rezultatuose.", + "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with search components added.": "%(brand)s trūksta kai kurių komponentų, reikalingų saugiai talpinti šifruotas žinutes lokaliai. Jei norite eksperimentuoti su šia funkcija, sukurkite pasirinktinį %(brand)s Desktop (darbastalio versiją), su pridėtais paieškos komponentais.", + "Manage": "Tvarkyti", + "rooms.": "kambarių.", + " to store messages from ": " saugoti žinutes iš ", + "Master private key:": "Pagrindinis privatus raktas:", + "not found in storage": "saugykloje nerasta", + "Cross-signing is not set up.": "Kryžminis pasirašymas nenustatytas.", + "Cross-signing is ready for use.": "Kryžminis pasirašymas yra paruoštas naudoti.", + "Channel: %(channelName)s": "Kanalas: %(channelName)s", + "Workspace: %(networkName)s": "Darbo sritis: %(networkName)s", + "This bridge is managed by .": "Šis tiltas yra tvarkomas .", + "This bridge was provisioned by .": "Šis tiltas buvo parūpintas .", + "Your server isn't responding to some requests.": "Jūsų serveris neatsako į kai kurias užklausas.", + "Unable to find a supported verification method.": "Nepavyko rasti palaikomo patvirtinimo metodo.", + "Start": "Pradėti", + "or": "arba", + "Incoming call": "Įeinantis skambutis", + "Incoming video call": "Įeinantis video skambutis", + "Incoming voice call": "Įeinantis balso skambutis", + "Unknown caller": "Nežinomas skambintojas", + "Downloading logs": "Parsiunčiami žurnalai", + "Uploading logs": "Įkeliami žurnalai", + "System font name": "Sistemos šrifto pavadinimas", + "Use a system font": "Naudoti sistemos šriftą", + "Use a more compact ‘Modern’ layout": "Naudoti labiau kompaktišką 'Modernų' išdėstymą", + "Use custom size": "Naudoti pasirinktinį dydį", + "Font size": "Šrifto dydis", + "Enable advanced debugging for the room list": "Įjungti išplėstinį kambarių sąrašo derinimą", + "If the other version of %(brand)s is still open in another tab, please close it as using %(brand)s on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Jei kita %(brand)s versija vis dar yra atidaryta kitame skirtuke, uždarykite jį, nes %(brand)s naudojimas tame pačiame serveryje, tuo pačiu metu įjungus ir išjungus tingų įkėlimą, sukelks problemų.", + "You've previously used %(brand)s 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, %(brand)s needs to resync your account.": "Jūs anksčiau naudojote %(brand)s ant %(host)s įjungę tingų narių įkėlimą. Šioje versijoje tingus įkėlimas yra išjungtas. Kadangi vietinė talpykla nesuderinama tarp šių dviejų nustatymų, %(brand)s reikia iš naujo sinchronizuoti jūsų paskyrą.", + "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.": "Jūs galite tai įjungti, jei kambarys bus naudojamas tik bendradarbiavimui su vidinėmis komandomis jūsų serveryje. Tai negali būti vėliau pakeista.", + "Your server requires encryption to be enabled in private rooms.": "Jūsų serveris reikalauja, kad šifravimas būtų įjungtas privačiuose kambariuose.", + "You don't currently have any stickerpacks enabled": "Jūs šiuo metu neturite jokių įjungtų lipdukų paketų", + "Enable experimental, compact IRC style layout": "Įjungti eksperimentinį, kompaktišką IRC stiliaus išdėstymą", + "Support adding custom themes": "Palaikykite pridėdami pasirinktines temas", + "New spinner design": "Naujas suktuko dizainas", + "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Bendruomenių v2 prototipai. Reikalingas suderinamas serveris. Itin eksperimentiniai - naudokite atsargiai.", + "%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s", + "%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s", + "%(senderName)s: %(message)s": "%(senderName)s: %(message)s", + "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", + "%(senderName)s is calling": "%(senderName)s skambina", + "Waiting for answer": "Laukiama atsakymo", + "%(senderName)s started a call": "%(senderName)s pradėjo skambutį", + "You started a call": "Jūs pradėjote skambutį", + "Call ended": "Skambutis baigtas", + "%(senderName)s left the call": "%(senderName)s paliko skambutį", + "You left the call": "Jūs palikote skambutį", + "Call in progress": "Vykdomas skambutis", + "The person who invited you already left the room, or their server is offline.": "Asmuo, kuris jus pakvietė, jau išėjo iš kambario, arba jo serveris yra neprisijungęs.", + "The person who invited you already left the room.": "Asmuo, kuris jus pakvietė, jau išėjo iš kambario.", + "Guest": "Svečias", + "A new version of %(brand)s is available!": "Galima nauja %(brand)s versija!", + "Upgrade your %(brand)s": "Atnaujinti jūsų %(brand)s", + "Restart": "Perkrauti", + "A widget would like to verify your identity": "Valdiklis nori patvirtinti jūsų tapatybę", + "Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Patvirtinant šį vartotoją, jo seansas bus pažymėtas kaip patikimas, taip pat jūsų seansas bus pažymėtas kaip patikimas jam.", + "In encrypted rooms, verify all users to ensure it’s secure.": "Užšifruotuose kambariuose, patvirtinkite visus vartotojus, kad užtikrintumėte jo saugumą.", + "The homeserver the user you’re verifying is connected to": "Serveris, prie kurio yra prisijungęs jūsų tvirtinamas vartotojas", + "Verify the link in your inbox": "Patvirtinkite nuorodą savo el. pašto dėžutėje", + "Click the link in the email you received to verify and then click continue again.": "Paspauskite nuorodą gautame el. laiške, kad patvirtintumėte, tada dar kartą spustelėkite tęsti.", + "Please verify the room ID or address and try again.": "Patikrinkite kambario ID arba adresą ir bandykite dar kartą.", + "Verify this session by confirming the following number appears on its screen.": "Patvirtinkite šį seansą, įsitikindami, kad jo ekrane rodomas toliau esantis skaičius." } From 6dc709a045204862279629d1e69f799537e27305 Mon Sep 17 00:00:00 2001 From: Resynth Date: Tue, 27 Oct 2020 20:10:23 +0000 Subject: [PATCH 0133/4306] =?UTF-8?q?=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Resynth --- res/themes/dark/css/_dark.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 6e0c9acdfe..fdf64d52f8 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -272,6 +272,10 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28); background-color: #080808; } } + + blockquote { + color: #919191; + } } // diff highlight colors From 89fbd33abf1f2bf3ea47bf5e9c6a232ef77833a8 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Tue, 27 Oct 2020 22:04:12 +0000 Subject: [PATCH 0134/4306] Translated using Weblate (Esperanto) Currently translated at 99.6% (2359 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index a3a4ece6b3..d6852e4426 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1407,7 +1407,7 @@ "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Bonvolu peti la administranton de via hejmservilo (%(homeserverDomain)s) agordi TURN-servilon, por ke vokoj funkciu dependeble.", "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.": "Alternative, vi povas prove uzi la publikan servilon je turn.matrix.org, sed tio ne estas same dependebla, kaj ĝi havigos vian IP-adreson al tiu servilo. Vi povas administri tion ankaŭ en Agordoj.", "Try using turn.matrix.org": "Provu uzi servilon turn.matrix.org", - "Sends a message as plain text, without interpreting it as markdown": "Sendas mesaĝon kiel platan tekston, sen interpreto al MarkDown", + "Sends a message as plain text, without interpreting it as markdown": "Sendas mesaĝon kiel platan tekston, sen interpreto al Markdown", "You do not have the required permissions to use this command.": "Vi ne havas sufiĉajn permesojn por uzi ĉi tiun komandon.", "Changes the avatar of the current room": "Ŝanĝas la profilbildon de la nuna ĉambro", "Use an identity server": "Uzi identigan servilon", @@ -2089,7 +2089,7 @@ "Review where you’re logged in": "Kontrolu, kie vi salutis", "New login. Was this you?": "Nova saluto. Ĉu tio estis vi?", "%(name)s is requesting verification": "%(name)s petas kontrolon", - "Sends a message as html, without interpreting it as markdown": "Sendas mesaĝon kiel HTML, ne interpretante ĝin kiel MarkDown", + "Sends a message as html, without interpreting it as markdown": "Sendas mesaĝon kiel HTML, ne interpretante ĝin kiel Markdown", "Failed to set topic": "Malsukcesis agordi temon", "Command failed": "Komando malsukcesis", "Could not find user in room": "Ne povis trovi uzanton en ĉambro", @@ -2522,7 +2522,7 @@ "End conference": "Fini grupan vokon", "The call was answered on another device.": "La voko estis respondita per alia aparato.", "Answered Elsewhere": "Respondita aliloke", - "The call could not be established": "Ne povis meti la vokon.", + "The call could not be established": "Ne povis meti la vokon", "The other party declined the call.": "La alia persono rifuzis la vokon.", "Call Declined": "Voko rifuziĝis" } From 24ba566877e5d9a897a3fe21a7b09cf860f0b5ab Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Wed, 28 Oct 2020 18:34:04 -0500 Subject: [PATCH 0135/4306] Specify community description img must be mxc urls Closes vector-im/element-web#7100 Signed-off-by: Matthew Kenigsberg --- src/components/structures/GroupView.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 482b9f6da2..bbc4187298 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -47,7 +47,7 @@ const LONG_DESC_PLACEHOLDER = _td( some important links

- You can even use 'img' tags + You can even add images with Matrix URLs

`); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1548dd5c13..600319a874 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2001,7 +2001,7 @@ "Attach files from chat or just drag and drop them anywhere in a room.": "Attach files from chat or just drag and drop them anywhere in a room.", "Communities": "Communities", "Create community": "Create community", - "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even use 'img' tags\n

\n": "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even use 'img' tags\n

\n", + "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even add images with Matrix URLs \n

\n": "

HTML for your community's page

\n

\n Use the long description to introduce new members to the community, or distribute\n some important links\n

\n

\n You can even add images with Matrix URLs \n

\n", "Add rooms to the community summary": "Add rooms to the community summary", "Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?", "Add to summary": "Add to summary", From c4e8b4221f7dd771794076d34511c6ba5b93140a Mon Sep 17 00:00:00 2001 From: MamasLT Date: Wed, 28 Oct 2020 14:43:42 +0000 Subject: [PATCH 0136/4306] Translated using Weblate (Lithuanian) Currently translated at 81.3% (1926 of 2367 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index 9adb7822ac..cdee2c3549 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1569,7 +1569,7 @@ "Import E2E room keys": "Importuoti E2E (visapusio šifravimo) kambarių raktus", "Session ID:": "Seanso ID:", "Session key:": "Seanso raktas:", - "%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s renka anoniminę analizę, kad galėtume patobulinti programą.", + "%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s renka anoniminius duomenis, kad mes galėtume tobulinti programą.", "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privatumas mums yra svarbus, todėl mes nerenkame jokių asmeninių ar identifikuojamų duomenų savo analitikai.", "Learn more about how we use analytics.": "Sužinokite daugiau apie tai, kaip mes naudojame analitiką.", "Reset": "Iš naujo nustatyti", @@ -1653,7 +1653,7 @@ "Set a new custom sound": "Nustatyti naują pasirinktinį garsą", "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.": "Paskyros deaktyvavimas, pagal nutylėjimą, nepriverčia mūsų sistemos užmiršti jūsų siųstų žinučių. Jei norite, kad jūsų žinutės mūsų sistemos būtų užmirštos, pažymėkite žemiau esantį laukelį.", "Use default": "Naudoti numatytąjį", - "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Serverio administratorius išjungė visapusį šifravimą kaip numatytą privačiuose kambariuose ir Tiesioginėse Žinutėse.", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Serverio administratorius išjungė visapusį šifravimą, kaip numatytą, privačiuose kambariuose ir Tiesioginėse Žinutėse.", "Notification sound": "Pranešimo garsas", "Sounds": "Garsai", "Privileged Users": "Privilegijuoti Nariai", @@ -2007,5 +2007,8 @@ "Verify the link in your inbox": "Patvirtinkite nuorodą savo el. pašto dėžutėje", "Click the link in the email you received to verify and then click continue again.": "Paspauskite nuorodą gautame el. laiške, kad patvirtintumėte, tada dar kartą spustelėkite tęsti.", "Please verify the room ID or address and try again.": "Patikrinkite kambario ID arba adresą ir bandykite dar kartą.", - "Verify this session by confirming the following number appears on its screen.": "Patvirtinkite šį seansą, įsitikindami, kad jo ekrane rodomas toliau esantis skaičius." + "Verify this session by confirming the following number appears on its screen.": "Patvirtinkite šį seansą, įsitikindami, kad jo ekrane rodomas toliau esantis skaičius.", + "Privacy": "Privatumas", + "Accept all %(invitedRooms)s invites": "Priimti visus %(invitedRooms)s pakvietimus", + "Bulk options": "Grupinės parinktys" } From 3f9f1d03c8445002e053ff15054aa538cc83c514 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Thu, 29 Oct 2020 13:22:09 +0000 Subject: [PATCH 0137/4306] stubbed isGuest for unit tests --- test/components/views/messages/TextualBody-test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 07cd51edbd..bf55e9c430 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -36,6 +36,7 @@ describe("", () => { MatrixClientPeg.matrixClient = { getRoom: () => mkStubRoom("room_id"), getAccountData: () => undefined, + isGuest: () => false, }; const ev = mkEvent({ @@ -59,6 +60,7 @@ describe("", () => { MatrixClientPeg.matrixClient = { getRoom: () => mkStubRoom("room_id"), getAccountData: () => undefined, + isGuest: () => false, }; const ev = mkEvent({ @@ -83,6 +85,7 @@ describe("", () => { MatrixClientPeg.matrixClient = { getRoom: () => mkStubRoom("room_id"), getAccountData: () => undefined, + isGuest: () => false, }; }); @@ -135,6 +138,7 @@ describe("", () => { getHomeserverUrl: () => "https://my_server/", on: () => undefined, removeListener: () => undefined, + isGuest: () => false, }; }); From f828c6d49467bcb9f9efc5104637a94b1ef13a6a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 29 Oct 2020 17:56:24 +0000 Subject: [PATCH 0138/4306] Implement call hold Currently just by adding /holdcall and /unholdcall slash commands The only place the hold status of the call is currently represented is when the call is a voice call and you're viewing a different room: it's not wired up when you're viewing the room because that currently uses the room status bar which it won't do with the new UI. Also convert VideoFeed to typescript, and remove videoview because it essentially just managed the fullscreen functionality, but we'll want and 'on hold' representation (and probably chrome for hagnup etc) in the fullscreen UI too, so let's just make CallView the thing that gets fullscreened. --- res/css/_components.scss | 4 +- res/css/views/voip/_CallContainer.scss | 4 +- res/css/views/voip/_CallView.scss | 7 + res/css/views/voip/_VideoView.scss | 49 ------ src/@types/global.d.ts | 14 ++ src/CallHandler.tsx | 25 ++- src/SlashCommands.tsx | 27 +++ src/components/structures/RoomView.tsx | 2 +- src/components/views/elements/AppTile.js | 2 +- src/components/views/voip/CallPreview.tsx | 2 +- src/components/views/voip/CallView.tsx | 196 ++++++++++++++-------- src/components/views/voip/VideoFeed.js | 58 ------- src/components/views/voip/VideoFeed.tsx | 82 +++++++++ src/components/views/voip/VideoView.js | 142 ---------------- src/i18n/strings/en_EN.json | 3 + 15 files changed, 287 insertions(+), 330 deletions(-) delete mode 100644 res/css/views/voip/_VideoView.scss delete mode 100644 src/components/views/voip/VideoFeed.js create mode 100644 src/components/views/voip/VideoFeed.tsx delete mode 100644 src/components/views/voip/VideoView.js diff --git a/res/css/_components.scss b/res/css/_components.scss index ad3cfbdcea..4a9301d085 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -9,6 +9,7 @@ @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; @import "./structures/_GenericErrorPage.scss"; +@import "./structures/_GroupFilterPanel.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @@ -27,7 +28,6 @@ @import "./structures/_ScrollPanel.scss"; @import "./structures/_SearchBox.scss"; @import "./structures/_TabbedView.scss"; -@import "./structures/_GroupFilterPanel.scss"; @import "./structures/_ToastContainer.scss"; @import "./structures/_UploadBar.scss"; @import "./structures/_UserMenu.scss"; @@ -227,4 +227,4 @@ @import "./views/verification/_VerificationShowSas.scss"; @import "./views/voip/_CallContainer.scss"; @import "./views/voip/_CallView.scss"; -@import "./views/voip/_VideoView.scss"; +@import "./views/voip/_VideoFeed.scss"; diff --git a/res/css/views/voip/_CallContainer.scss b/res/css/views/voip/_CallContainer.scss index 759797ae7b..eec8a1f188 100644 --- a/res/css/views/voip/_CallContainer.scss +++ b/res/css/views/voip/_CallContainer.scss @@ -33,11 +33,11 @@ limitations under the License. pointer-events: initial; // restore pointer events so the user can leave/interact cursor: pointer; - .mx_VideoView { + .mx_CallView_video { width: 350px; } - .mx_VideoView_localVideoFeed { + .mx_VideoFeed_local { border-radius: 8px; overflow: hidden; } diff --git a/res/css/views/voip/_CallView.scss b/res/css/views/voip/_CallView.scss index f6f3d40308..2aeaaa87dc 100644 --- a/res/css/views/voip/_CallView.scss +++ b/res/css/views/voip/_CallView.scss @@ -92,3 +92,10 @@ limitations under the License. background-color: $primary-fg-color; } } + +.mx_CallView_video { + width: 100%; + position: relative; + z-index: 30; +} + diff --git a/res/css/views/voip/_VideoView.scss b/res/css/views/voip/_VideoView.scss deleted file mode 100644 index feb60f4763..0000000000 --- a/res/css/views/voip/_VideoView.scss +++ /dev/null @@ -1,49 +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. -*/ - -.mx_VideoView { - width: 100%; - position: relative; - z-index: 30; -} - -.mx_VideoView video { - width: 100%; -} - -.mx_VideoView_remoteVideoFeed { - width: 100%; - background-color: #000; - z-index: 50; -} - -.mx_VideoView_localVideoFeed { - width: 25%; - height: 25%; - position: absolute; - left: 10px; - bottom: 10px; - z-index: 100; -} - -.mx_VideoView_localVideoFeed video { - width: auto; - height: 100%; -} - -.mx_VideoView_localVideoFeed.mx_VideoView_localVideoFeed_flipped video { - transform: scale(-1, 1); -} diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index ed28a5c479..acb2c40031 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -65,6 +65,13 @@ declare global { interface Document { // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasStorageAccess hasStorageAccess?: () => Promise; + + // Safari & IE11 only have this prefixed: we used prefixed versions + // previously so let's continue to support them for now + webkitExitFullscreen(): Promise; + msExitFullscreen(): Promise; + readonly webkitFullscreenElement: Element | null; + readonly msFullscreenElement: Element | null; } interface Navigator { @@ -94,4 +101,11 @@ declare global { interface HTMLAudioElement { type?: string; } + + interface Element { + // Safari & IE11 only have this prefixed: we used prefixed versions + // previously so let's continue to support them for now + webkitRequestFullScreen(options?: FullscreenOptions): Promise; + msRequestFullscreen(options?: FullscreenOptions): Promise; + } } diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index e303dd3819..17867536ed 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -59,8 +59,7 @@ import {MatrixClientPeg} from './MatrixClientPeg'; import PlatformPeg from './PlatformPeg'; import Modal from './Modal'; import { _t } from './languageHandler'; -// @ts-ignore - XXX: tsc doesn't like this: our js-sdk imports are complex so this isn't surprising -import Matrix from 'matrix-js-sdk'; +import Matrix from 'matrix-js-sdk/src/browser-index'; import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; @@ -77,7 +76,7 @@ import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import WidgetStore from "./stores/WidgetStore"; import { WidgetMessagingStore } from "./stores/widgets/WidgetMessagingStore"; import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions"; -import { MatrixCall, CallErrorCode, CallState, CallEvent, CallParty } from "matrix-js-sdk/lib/webrtc/call"; +import { MatrixCall, CallErrorCode, CallState, CallEvent, CallParty } from "matrix-js-sdk/src/webrtc/call"; import Analytics from './Analytics'; enum AudioID { @@ -97,6 +96,18 @@ export enum PlaceCallType { ScreenSharing = 'screensharing', } +function getRemoteAudioElement(): HTMLAudioElement { + // this needs to be somewhere at the top of the DOM which + // always exists to avoid audio interruptions. + // Might as well just use DOM. + const remoteAudioElement = document.getElementById("remoteAudio") as HTMLAudioElement; + if (!remoteAudioElement) { + console.error("Failed to find remoteAudio element - cannot play audio!" + + "You need to add an