From 29a81bbe85ca1ca1c4c54534fe94e9d477aef22c Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Tue, 10 Nov 2020 15:04:01 -0600 Subject: [PATCH 001/863] Warn when you attempt to leave room that you are the only member of Signed-off-by: Aaron Raimist --- src/components/structures/MatrixChat.tsx | 17 ++++++++++++++++- src/i18n/strings/en_EN.json | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 22cd73eff7..43e1798c6e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1056,8 +1056,21 @@ export default class MatrixChat extends React.PureComponent { private leaveRoomWarnings(roomId: string) { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); // Show a warning if there are additional complications. - const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); const warnings = []; + + const memberCount = roomToLeave.currentState.getJoinedMemberCount(); + if (memberCount === 1) { + warnings.push( + + {' '/* Whitespace, otherwise the sentences get smashed together */ } + { _t("You are the only member of this room. This room will become unjoinable if you leave.") } + + ); + + return warnings; + } + + const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); if (joinRules) { const rule = joinRules.getContent().join_rule; if (rule !== "public") { @@ -1076,6 +1089,7 @@ export default class MatrixChat extends React.PureComponent { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const roomToLeave = MatrixClientPeg.get().getRoom(roomId); const warnings = this.leaveRoomWarnings(roomId); + const hasWarnings = warnings.length > 0; Modal.createTrackedDialog('Leave room', '', QuestionDialog, { title: _t("Leave room"), @@ -1086,6 +1100,7 @@ export default class MatrixChat extends React.PureComponent { ), button: _t("Leave"), + danger: hasWarnings, onFinished: (shouldLeave) => { if (shouldLeave) { const d = leaveRoomBehaviour(roomId); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 78340447f3..b412db5ca0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2317,6 +2317,7 @@ "Cannot create rooms in this community": "Cannot create rooms in this community", "You do not have permission to create rooms in this community.": "You do not have permission to create rooms in this community.", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", + "You are the only member of this room. This room will become unjoinable if you leave.": "You are the only member of this room. This room will become unjoinable if you leave.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", "Signed Out": "Signed Out", From 80c4d54ccc56396e15a4979de84d9d18c83a70ad Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 18 Nov 2020 13:54:49 -0600 Subject: [PATCH 002/863] Fix lint Signed-off-by: Aaron Raimist --- src/components/structures/MatrixChat.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 43e1798c6e..17c21a2016 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1060,12 +1060,12 @@ export default class MatrixChat extends React.PureComponent { const memberCount = roomToLeave.currentState.getJoinedMemberCount(); if (memberCount === 1) { - warnings.push( + warnings.push(( {' '/* Whitespace, otherwise the sentences get smashed together */ } { _t("You are the only member of this room. This room will become unjoinable if you leave.") } - ); + )); return warnings; } From 89bc4435945bfb207355cf5e5e290925f7d7f7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 16:02:27 +0100 Subject: [PATCH 003/863] Fix file drop UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 2 +- res/css/structures/_RoomView.scss | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index ad1656efbb..3de68b000d 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -22,7 +22,7 @@ limitations under the License. } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { - padding: 5px; + padding: 0 5px 5px 5px; // margin left to not allow the handle to not encroach on the space for the scrollbar margin-left: 8px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 572c7166d2..0a70b027ae 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -23,24 +23,21 @@ limitations under the License. .mx_RoomView_fileDropTarget { min-width: 0px; width: 100%; + height: 100%; + + margin-left: 6.25px; + font-size: $font-18px; text-align: center; pointer-events: none; - padding-left: 12px; - padding-right: 12px; - margin-left: -12px; - border-top-left-radius: 10px; border-top-right-radius: 10px; background-color: $droptarget-bg-color; - border: 2px #e1dddd solid; - border-bottom: none; + position: absolute; - top: 52px; - bottom: 0px; z-index: 3000; } From 41e2ffdf0df43104ef171b690b344a6e22b286f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 19:51:49 +0100 Subject: [PATCH 004/863] Added background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 0a70b027ae..9292a400bc 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -39,13 +39,19 @@ limitations under the License. position: absolute; z-index: 3000; + + display: flex; + justify-content: center; + align-items: center; } .mx_RoomView_fileDropTargetLabel { - top: 50%; - width: 100%; - margin-top: -50px; position: absolute; + + border-radius: 10px; + padding: 10px; + + background-color: $menu-bg-color; } .mx_RoomView_auxPanel { From da97d18332c5740499913506b0e059e5b4c7616c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 21:33:05 +0100 Subject: [PATCH 005/863] Added a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 9292a400bc..dd63be3a11 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -25,6 +25,7 @@ limitations under the License. width: 100%; height: 100%; + // This is an ugly fix for centering this element margin-left: 6.25px; font-size: $font-18px; From dcb30b72b0ed1adc6fb075ee9cc26ca0338177bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 13:25:22 +0100 Subject: [PATCH 006/863] Fix left panel resizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 32 ++++++++++++++++---------- res/css/structures/_RoomView.scss | 12 ++++++---- src/components/structures/RoomView.tsx | 28 +++++++++++----------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 3de68b000d..6875ef12e0 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -22,22 +22,30 @@ limitations under the License. } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { - padding: 0 5px 5px 5px; - // margin left to not allow the handle to not encroach on the space for the scrollbar - margin-left: 8px; + padding: 0 5px 5px 0px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel + + .mx_RightPanel_ResizeHandle { + width: 9px; + } &:hover .mx_RightPanel_ResizeHandle { - // Need to use important to override element style attributes - // set by re-resizable - top: 50% !important; - transform: translate(0, -50%); + &::before { + position: absolute; + left: 6px; + top: 50%; + transform: translate(0, -50%); - height: 64px !important; // to match width of the ones on roomlist - width: 4px !important; - border-radius: 4px !important; + height: 64px; + width: 4px; + border-radius: 4px; - background-color: $primary-fg-color; - opacity: 0.8; + content: ' '; + + background-color: $primary-fg-color; + opacity: 0.8; + + margin-left: -10px; + } } } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index dd63be3a11..0a12a86c33 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -25,9 +25,6 @@ limitations under the License. width: 100%; height: 100%; - // This is an ugly fix for centering this element - margin-left: 6.25px; - font-size: $font-18px; text-align: center; @@ -120,16 +117,23 @@ limitations under the License. height: 50px; } -.mx_RoomView_body { +.mx_RoomView_container { position: relative; //for .mx_RoomView_auxPanel_fullHeight display: flex; flex-direction: column; +} + +.mx_RoomView_body { + display: flex; + flex-direction: column; flex: 1; min-width: 0; .mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner { order: 2; } + + margin-right: 10px; } .mx_RoomView_body .mx_RoomView_timeline { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0ee847fbc9..3d62c06e4b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2003,22 +2003,24 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel} -
- {topUnreadMessagesBar} - {jumpToBottom} - {messagePanel} - {searchResultsPanel} -
-
-
-
- {statusBar} +
+
+ {topUnreadMessagesBar} + {jumpToBottom} + {messagePanel} + {searchResultsPanel}
+
+
+
+ {statusBar} +
+
+ {previewBar} + {messageComposer}
- {previewBar} - {messageComposer}
From e70dee08d0ea7b303a51fb807929376b2dad79dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 19:50:59 +0100 Subject: [PATCH 007/863] Fix flickering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 42 ++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 3d62c06e4b..67f9663597 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -187,6 +187,7 @@ export interface IState { rejecting?: boolean; rejectError?: Error; hasPinnedWidgets?: boolean; + dragCounter: number; } export default class RoomView extends React.Component { @@ -237,6 +238,7 @@ export default class RoomView extends React.Component { canReply: false, useIRCLayout: SettingsStore.getValue("useIRCLayout"), matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), + dragCounter: 0, }; this.dispatcherRef = dis.register(this.onAction); @@ -525,8 +527,8 @@ export default class RoomView extends React.Component { if (!roomView.ondrop) { roomView.addEventListener('drop', this.onDrop); roomView.addEventListener('dragover', this.onDragOver); - roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.addEventListener('dragend', this.onDragLeaveOrEnd); + roomView.addEventListener('dragenter', this.onDragEnter); + roomView.addEventListener('dragleave', this.onDragLeave); } } @@ -1108,6 +1110,31 @@ export default class RoomView extends React.Component { this.updateTopUnreadMessagesBar(); }; + private onDragEnter = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + this.setState({ + dragCounter: this.state.dragCounter + 1, + draggingFile: true, + }); + }; + + private onDragLeave = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + this.setState({ + dragCounter: this.state.dragCounter - 1, + }); + + if (this.state.dragCounter == 0) { + this.setState({ + draggingFile: false, + }); + } + }; + private onDragOver = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -1115,7 +1142,6 @@ export default class RoomView extends React.Component { ev.dataTransfer.dropEffect = 'none'; if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) { - this.setState({ draggingFile: true }); ev.dataTransfer.dropEffect = 'copy'; } }; @@ -1126,14 +1152,12 @@ export default class RoomView extends React.Component { ContentMessages.sharedInstance().sendContentListToRoom( ev.dataTransfer.files, this.state.room.roomId, this.context, ); - this.setState({ draggingFile: false }); dis.fire(Action.FocusComposer); - }; - private onDragLeaveOrEnd = ev => { - ev.stopPropagation(); - ev.preventDefault(); - this.setState({ draggingFile: false }); + this.setState({ + draggingFile: false, + dragCounter: this.state.dragCounter - 1, + }); }; private injectSticker(url, info, text) { From 044e02b06ad46b417d3aa8fc33f24c1374fdcb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:16:53 +0100 Subject: [PATCH 008/863] Remove spaces in empty line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 6875ef12e0..f05f24d0d7 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -24,7 +24,7 @@ limitations under the License. .mx_MainSplit > .mx_RightPanel_ResizeWrapper { padding: 0 5px 5px 0px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel - + .mx_RightPanel_ResizeHandle { width: 9px; } From 365d252d3f0eb64755f502318c95f855a4404f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:25:12 +0100 Subject: [PATCH 009/863] Fix removing event listeners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 67f9663597..d910940a73 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -572,8 +572,8 @@ export default class RoomView extends React.Component { const roomView = this.roomView.current; roomView.removeEventListener('drop', this.onDrop); roomView.removeEventListener('dragover', this.onDragOver); - roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.removeEventListener('dragend', this.onDragLeaveOrEnd); + roomView.removeEventListener('dragenter', this.onDragEnter); + roomView.removeEventListener('dragleave', this.onDragLeave); } dis.unregister(this.dispatcherRef); if (this.context) { From 5d7e45e6cf85e14f4143923f7e29642f97965fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:29:33 +0100 Subject: [PATCH 010/863] Added dragCounter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/contexts/RoomContext.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 082dcc4e6b..1b9097e337 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -42,6 +42,7 @@ const RoomContext = createContext({ canReply: false, useIRCLayout: false, matrixClientIsReady: false, + dragCounter: 0, }); RoomContext.displayName = "RoomContext"; export default RoomContext; From 8aabe1f33077ef95f4414c32b3b625b4297369ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 09:26:09 +0100 Subject: [PATCH 011/863] Reorganized elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 114 +++++---------------- src/components/views/elements/ImageView.js | 60 +++++------ 2 files changed, 54 insertions(+), 120 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 0a4ed2a194..77fa597d66 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -22,45 +22,46 @@ limitations under the License. display: flex; width: 100%; height: 100%; - align-items: center; -} - -.mx_ImageView_lhs { - order: 1; - flex: 1 1 10%; - min-width: 60px; - // background-color: #080; - // height: 20px; } .mx_ImageView_content { - order: 2; - /* min-width hack needed for FF */ - min-width: 0px; - height: 90%; - flex: 15 15 0; + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; +} + +.mx_ImageView_imageBox { + overflow: auto; + margin: 0 50px 50px 50px; + flex: 1; display: flex; - align-items: center; - justify-content: center; } .mx_ImageView_content img { - max-width: 100%; + flex: 1; + //max-width: 100%; /* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */ - max-height: 100%; + //max-height: 100%; /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; /* background-image: url('$(res)/img/trans.png'); */ pointer-events: all; } -.mx_ImageView_labelWrapper { - position: absolute; - top: 0px; - right: 0px; - height: 100%; - overflow: auto; - pointer-events: all; +.mx_ImageView_panel { + display: flex; + justify-content: space-between; + padding: 50px; +} + +.mx_ImageView_toolbar { + display: flex; +} + +.mx_ImageView_button { + padding: 5px; } .mx_ImageView_label { @@ -68,39 +69,11 @@ limitations under the License. display: flex; justify-content: center; flex-direction: column; - padding-left: 30px; - padding-right: 30px; min-height: 100%; max-width: 240px; color: $lightbox-fg-color; } -.mx_ImageView_cancel { - position: absolute; - // hack for mx_Dialog having a top padding of 40px - top: 40px; - right: 0px; - padding-top: 35px; - padding-right: 35px; - cursor: pointer; -} - -.mx_ImageView_rotateClockwise { - position: absolute; - top: 40px; - right: 70px; - padding-top: 35px; - cursor: pointer; -} - -.mx_ImageView_rotateCounterClockwise { - position: absolute; - top: 40px; - right: 105px; - padding-top: 35px; - cursor: pointer; -} - .mx_ImageView_name { font-size: $font-18px; margin-bottom: 6px; @@ -112,41 +85,6 @@ limitations under the License. opacity: 0.5; } -.mx_ImageView_download { - display: table; - margin-top: 24px; - margin-bottom: 6px; - border-radius: 5px; - background-color: $lightbox-bg-color; - font-size: $font-14px; - padding: 9px; - border: 1px solid $lightbox-border-color; -} - .mx_ImageView_size { font-size: $font-11px; } - -.mx_ImageView_link { - color: $lightbox-fg-color !important; - text-decoration: none !important; -} - -.mx_ImageView_button { - font-size: $font-15px; - opacity: 0.5; - margin-top: 18px; - cursor: pointer; -} - -.mx_ImageView_shim { - height: 30px; -} - -.mx_ImageView_rhs { - order: 3; - flex: 1 1 10%; - min-width: 300px; - // background-color: #800; - // height: 20px; -} diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e39075cedc..a66d2e2e6f 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -153,7 +153,7 @@ export default class ImageView extends React.Component { let mayRedact = false; const showEventMeta = !!this.props.mxEvent; - let eventMeta; + let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid let sender = this.props.mxEvent.getSender(); @@ -165,7 +165,7 @@ export default class ImageView extends React.Component { if (member) sender = member.name; } - eventMeta = (
+ metadata = (
{ _t('Uploaded on %(date)s by %(user)s', { date: formatDate(new Date(this.props.mxEvent.getTs())), user: sender, @@ -173,11 +173,13 @@ export default class ImageView extends React.Component {
); } - let eventRedact; + let redactButton; if (mayRedact) { - eventRedact = (
- { _t('Remove') } -
); + redactButton = ( + + { + + ); } const rotationDegrees = this.state.rotationDegrees; @@ -192,40 +194,34 @@ export default class ImageView extends React.Component { }} className="mx_ImageView" > -
-
- -
+
- - { - - - { - - - { - -
-
{ this.getName() }
- { eventMeta } - -
- { _t('Download this file') }
- { sizeRes } -
+ { metadata } + { sizeRes } +
+
+ + { + + + { + + + { - { eventRedact } -
-
+ { redactButton } + + { +
-
-
+
+ +
); From 2040815f661be9c832a629022f01764ae99c8c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 10:20:15 +0100 Subject: [PATCH 012/863] Implement zooming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 45 +++++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index a66d2e2e6f..0c235d87be 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -46,7 +46,10 @@ export default class ImageView extends React.Component { constructor(props) { super(props); - this.state = { rotationDegrees: 0 }; + this.state = { + rotationDegrees: 0, + zoom: 100, + }; } onKeyDown = (ev) => { @@ -57,6 +60,16 @@ export default class ImageView extends React.Component { } }; + onWheel = (ev) => { + if (ev.ctrlKey) { + ev.stopPropagation(); + ev.preventDefault(); + this.setState({ + zoom: this.state.zoom - ev.deltaY, + }); + } + } + onRedactClick = () => { const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, { @@ -98,6 +111,18 @@ export default class ImageView extends React.Component { this.setState({ rotationDegrees }); }; + zoomIn = () => { + this.setState({ + zoom: this.state.zoom + 10, + }); + }; + + zoomOut = () => { + this.setState({ + zoom: this.state.zoom - 10, + }); + } + render() { /* // In theory max-width: 80%, max-height: 80% on the CSS should work @@ -130,12 +155,13 @@ export default class ImageView extends React.Component { let style = {}; let res; + style = { + width: this.state.zoom + "%", + height: this.state.zoom + "%", + }; + if (this.props.width && this.props.height) { - style = { - width: this.props.width, - height: this.props.height, - }; - res = style.width + "x" + style.height + "px"; + res = this.props.width + "x" + this.props.height + "px"; } let size; @@ -190,6 +216,7 @@ export default class ImageView extends React.Component { returnFocus={true} lockProps={{ onKeyDown: this.onKeyDown, + onWheel: this.onWheel, role: "dialog", }} className="mx_ImageView" @@ -204,6 +231,12 @@ export default class ImageView extends React.Component { { sizeRes }
+ + { + + + { + { From 3c306bc54bb81aea6fa18d3f7204c083a053a0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 12:54:55 +0100 Subject: [PATCH 013/863] Added icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/download-white.svg | 95 +++++++++++++++++++++ res/img/feather-customised/trash.custom.svg | 87 +++++++++++++++++-- res/img/minus-white.svg | 64 ++++++++++++++ res/img/plus-white.svg | 73 ++++++++++++++++ res/img/trash-red.svg | 89 +++++++++++++++++++ src/components/views/elements/ImageView.js | 8 +- 6 files changed, 406 insertions(+), 10 deletions(-) create mode 100644 res/img/download-white.svg create mode 100644 res/img/minus-white.svg create mode 100644 res/img/plus-white.svg create mode 100644 res/img/trash-red.svg diff --git a/res/img/download-white.svg b/res/img/download-white.svg new file mode 100644 index 0000000000..5c800b350e --- /dev/null +++ b/res/img/download-white.svg @@ -0,0 +1,95 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + + + + + + + + + + + diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index dc1985ddb2..589bb0a4e5 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -1,7 +1,82 @@ - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/res/img/minus-white.svg b/res/img/minus-white.svg new file mode 100644 index 0000000000..2921f34980 --- /dev/null +++ b/res/img/minus-white.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + diff --git a/res/img/plus-white.svg b/res/img/plus-white.svg new file mode 100644 index 0000000000..7759ace50a --- /dev/null +++ b/res/img/plus-white.svg @@ -0,0 +1,73 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + + diff --git a/res/img/trash-red.svg b/res/img/trash-red.svg new file mode 100644 index 0000000000..0b1d201d2e --- /dev/null +++ b/res/img/trash-red.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0c235d87be..719a17d3cb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -203,7 +203,7 @@ export default class ImageView extends React.Component { if (mayRedact) { redactButton = ( - { + { ); } @@ -232,10 +232,10 @@ export default class ImageView extends React.Component {
- { + { - { + { { @@ -244,7 +244,7 @@ export default class ImageView extends React.Component { { - { + { { redactButton } From b9f480a825f8f8279d807aa2d10973a58fef78b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:13:34 +0100 Subject: [PATCH 014/863] Remove flex property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 77fa597d66..5f002c7d7a 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -35,7 +35,6 @@ limitations under the License. .mx_ImageView_imageBox { overflow: auto; margin: 0 50px 50px 50px; - flex: 1; display: flex; } From 2c5f3f31b1bd339c6d40337ddc3bd059fe095edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:30:56 +0100 Subject: [PATCH 015/863] Fixed Chromium issues - made listner non-passive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 719a17d3cb..6dfd1f78ad 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -52,6 +52,14 @@ export default class ImageView extends React.Component { }; } + componentDidMount() { + this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); + } + + componentWillUnmount() { + this.focusLock.removeEventListener('wheel', this.onWheel); + } + onKeyDown = (ev) => { if (ev.key === Key.ESCAPE) { ev.stopPropagation(); @@ -216,10 +224,10 @@ export default class ImageView extends React.Component { returnFocus={true} lockProps={{ onKeyDown: this.onKeyDown, - onWheel: this.onWheel, role: "dialog", }} className="mx_ImageView" + ref={ref => this.focusLock = ref} >
From 633221f012961024d2e706f7b7ee0c1fb3cb88f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:50:21 +0100 Subject: [PATCH 016/863] Center image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 5f002c7d7a..e6ed3684bb 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -40,13 +40,9 @@ limitations under the License. .mx_ImageView_content img { flex: 1; - //max-width: 100%; - /* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */ - //max-height: 100%; - /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; - /* background-image: url('$(res)/img/trans.png'); */ pointer-events: all; + margin: auto; } .mx_ImageView_panel { From ae25ff82169048be25dcb37c8ec680b22dfb805d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 15:54:26 +0100 Subject: [PATCH 017/863] Switched to scale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 5 +++-- src/components/views/elements/ImageView.js | 9 ++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index e6ed3684bb..c87cfd1ece 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -36,13 +36,14 @@ limitations under the License. overflow: auto; margin: 0 50px 50px 50px; display: flex; + height: 100%; } .mx_ImageView_content img { - flex: 1; object-fit: contain; pointer-events: all; - margin: auto; + //margin: auto; + //margin: 0 auto 0 auto; } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 6dfd1f78ad..a42d957daa 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -160,14 +160,8 @@ export default class ImageView extends React.Component { height: displayHeight }; */ - let style = {}; let res; - style = { - width: this.state.zoom + "%", - height: this.state.zoom + "%", - }; - if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; } @@ -217,7 +211,8 @@ export default class ImageView extends React.Component { } const rotationDegrees = this.state.rotationDegrees; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; + const zoom = this.state.zoom/100; + const effectiveStyle = {transform: `rotate(${rotationDegrees}deg) scale(${zoom})`}; return ( Date: Sun, 20 Dec 2020 09:51:57 +0100 Subject: [PATCH 018/863] Added a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index a42d957daa..bc1d8b4d53 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -53,6 +53,8 @@ export default class ImageView extends React.Component { } componentDidMount() { + // We have to use addEventListener() because the listener + // needs to be passive in order to work with Chromium this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } From be9b68a4dd3d453548b5e1562019998bd28170de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 10:07:03 +0100 Subject: [PATCH 019/863] Use height and width properties for scaling again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 11 ++++++----- src/components/views/elements/ImageView.js | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index c87cfd1ece..1fcf1bf543 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -33,17 +33,18 @@ limitations under the License. } .mx_ImageView_imageBox { - overflow: auto; - margin: 0 50px 50px 50px; + overflow: scroll; display: flex; - height: 100%; + flex-grow: 1; +} + +.mainImage { + margin: auto; } .mx_ImageView_content img { object-fit: contain; pointer-events: all; - //margin: auto; - //margin: 0 auto 0 auto; } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index bc1d8b4d53..2feff97fef 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -163,6 +163,10 @@ export default class ImageView extends React.Component { }; */ let res; + const style = { + height: this.state.zoom + "%", + width: this.state.zoom + "%", + }; if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; @@ -213,8 +217,7 @@ export default class ImageView extends React.Component { } const rotationDegrees = this.state.rotationDegrees; - const zoom = this.state.zoom/100; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg) scale(${zoom})`}; + const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; return ( Date: Sun, 20 Dec 2020 12:16:05 +0100 Subject: [PATCH 020/863] Added some padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 1fcf1bf543..ec651fcc6a 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -35,6 +35,7 @@ limitations under the License. .mx_ImageView_imageBox { overflow: scroll; display: flex; + padding: 0 50px 50px 50px; flex-grow: 1; } From f9884b1cc75cd7b2cbdb1d15cfa9b9ea364f424c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 17:40:16 +0100 Subject: [PATCH 021/863] Implement translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 +- src/components/views/elements/ImageView.js | 144 +++++++++++++-------- 2 files changed, 97 insertions(+), 53 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ec651fcc6a..10553a1c54 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -33,7 +33,7 @@ limitations under the License. } .mx_ImageView_imageBox { - overflow: scroll; + overflow: hidden; display: flex; padding: 0 50px 50px 50px; flex-grow: 1; @@ -41,6 +41,10 @@ limitations under the License. .mainImage { margin: auto; + + &:hover { + cursor: grab; + } } .mx_ImageView_content img { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 2feff97fef..8cc55e40bf 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -47,11 +47,19 @@ export default class ImageView extends React.Component { constructor(props) { super(props); this.state = { - rotationDegrees: 0, + rotation: 0, zoom: 100, + translationX: 0, + translationY: 0, + moving: false, }; } + initX = 0; + initY = 0; + lastX = 0; + lastY = 0; + componentDidMount() { // We have to use addEventListener() because the listener // needs to be passive in order to work with Chromium @@ -74,8 +82,18 @@ export default class ImageView extends React.Component { if (ev.ctrlKey) { ev.stopPropagation(); ev.preventDefault(); + const newZoom =this.state.zoom - ev.deltaY; + + if (newZoom <= 100) { + this.setState({ + zoom: 100, + translationX: 0, + translationY: 0, + }); + return; + } this.setState({ - zoom: this.state.zoom - ev.deltaY, + zoom: newZoom, }); } } @@ -109,65 +127,72 @@ export default class ImageView extends React.Component { return name; } - rotateCounterClockwise = () => { - const cur = this.state.rotationDegrees; + onRotateCounterClockwiseClick = () => { + const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; - this.setState({ rotationDegrees }); + this.setState({ rotation: rotationDegrees }); }; - rotateClockwise = () => { - const cur = this.state.rotationDegrees; + onRotateClockwiseClick = () => { + const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; - this.setState({ rotationDegrees }); + this.setState({ rotation: rotationDegrees }); }; - zoomIn = () => { + onZoomInClick = () => { this.setState({ zoom: this.state.zoom + 10, }); }; - zoomOut = () => { + onZoomOutClick = () => { + if (this.state.zoom <= 100) { + this.setState({ + zoom: 100, + translationX: 0, + translationY: 0, + }); + return; + } this.setState({ zoom: this.state.zoom - 10, }); } + onStartMoving = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + if (this.state.zoom <= 100) return false; + + this.setState({moving: true}); + this.initX = ev.pageX - this.lastX; + this.initY = ev.pageY - this.lastY; + } + + onMoving = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + if (!this.state.moving) return false; + + this.lastX = ev.pageX - this.initX; + this.lastY = ev.pageY - this.initY; + this.setState({ + translationX: this.lastX, + translationY: this.lastY, + }); + } + + onEndMoving = ev => { + this.setState({moving: false}); + } + render() { -/* - // In theory max-width: 80%, max-height: 80% on the CSS should work - // but in practice, it doesn't, so do it manually: + let mayRedact = false; + const showEventMeta = !!this.props.mxEvent; - var width = this.props.width || 500; - var height = this.props.height || 500; - - var maxWidth = document.documentElement.clientWidth * 0.8; - var maxHeight = document.documentElement.clientHeight * 0.8; - - var widthFrac = width / maxWidth; - var heightFrac = height / maxHeight; - - var displayWidth; - var displayHeight; - if (widthFrac > heightFrac) { - displayWidth = Math.min(width, maxWidth); - displayHeight = (displayWidth / width) * height; - } else { - displayHeight = Math.min(height, maxHeight); - displayWidth = (displayHeight / height) * width; - } - - var style = { - width: displayWidth, - height: displayHeight - }; -*/ let res; - const style = { - height: this.state.zoom + "%", - width: this.state.zoom + "%", - }; - if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; } @@ -184,9 +209,6 @@ export default class ImageView extends React.Component { sizeRes = size || res; } - let mayRedact = false; - const showEventMeta = !!this.props.mxEvent; - let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid @@ -216,8 +238,16 @@ export default class ImageView extends React.Component { ); } - const rotationDegrees = this.state.rotationDegrees; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; + const rotationDegrees = this.state.rotation + "deg"; + const zoomPercentage = this.state.zoom/100; + const translatePixelsX = this.state.translationX + "px"; + const translatePixelsY = this.state.translationY + "px"; + const style = { + transform: `rotate(${rotationDegrees}) + scale(${zoomPercentage}) + translateX(${translatePixelsX}) + translateY(${translatePixelsY})`, + }; return ( { sizeRes }
- +
From 7dd7aeffedc612827712a0b1ff91a3561f56f016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 18:19:11 +0100 Subject: [PATCH 022/863] Remove imageBox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 10 +++------ src/components/views/elements/ImageView.js | 24 ++++++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 10553a1c54..a2ca9c7927 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -26,21 +26,15 @@ limitations under the License. .mx_ImageView_content { width: 100%; - height: 100%; display: flex; flex-direction: column; -} -.mx_ImageView_imageBox { overflow: hidden; - display: flex; - padding: 0 50px 50px 50px; - flex-grow: 1; } .mainImage { - margin: auto; + //margin: auto; &:hover { cursor: grab; @@ -56,6 +50,8 @@ limitations under the License. display: flex; justify-content: space-between; padding: 50px; + position: absolute; + width: 100%; } .mx_ImageView_toolbar { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8cc55e40bf..9d747463f9 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -290,19 +290,17 @@ export default class ImageView extends React.Component {
-
- -
+
); From 6758734593ad30e9a766e63ccb23921b43c00c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 19:48:24 +0100 Subject: [PATCH 023/863] Remove panel element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 39 +++++++++------- src/components/views/elements/ImageView.js | 54 +++++++++++----------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index a2ca9c7927..0153d372fc 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -28,50 +28,55 @@ limitations under the License. width: 100%; display: flex; - flex-direction: column; + justify-content: center; + align-items: center; overflow: hidden; } .mainImage { - //margin: auto; + object-fit: contain; + pointer-events: all; + + max-width: 100vw; + max-height: 90vh; + min-width: 100px; + min-height: 100px; &:hover { cursor: grab; } } -.mx_ImageView_content img { - object-fit: contain; +.mx_ImageView_panel { + position: absolute; + z-index: 1000; + align-self: flex-start; pointer-events: all; } -.mx_ImageView_panel { - display: flex; - justify-content: space-between; - padding: 50px; - position: absolute; - width: 100%; -} - .mx_ImageView_toolbar { + right: 0; + padding: 50px 50px 0 0; display: flex; -} - -.mx_ImageView_button { - padding: 5px; + } .mx_ImageView_label { + left: 0; + padding: 50px 0 0 50px; text-align: left; display: flex; justify-content: center; flex-direction: column; - min-height: 100%; max-width: 240px; color: $lightbox-fg-color; } +.mx_ImageView_button { + padding: 5px; +} + .mx_ImageView_name { font-size: $font-18px; margin-bottom: 6px; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 9d747463f9..86c6cb8ad5 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -260,35 +260,33 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
-
-
- { this.getName() } -
- { metadata } - { sizeRes } -
-
- - { - - - { - - - { - - - { - - - { - - { redactButton } - - { - +
+
+ { this.getName() }
+ { metadata } + { sizeRes } +
+
+ + { + + + { + + + { + + + { + + + { + + { redactButton } + + { +
Date: Sun, 20 Dec 2020 20:00:11 +0100 Subject: [PATCH 024/863] Fixed translation issue while the image is rotated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 86c6cb8ad5..0db907003c 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -242,11 +242,15 @@ export default class ImageView extends React.Component { const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; const translatePixelsY = this.state.translationY + "px"; + /* The order of the values is important! + * First, we translate and only then we rotate, otherwise + * we would apply the translation to an already rotated + * image causing it translate in the wrong direction. */ const style = { - transform: `rotate(${rotationDegrees}) + transform: `translateX(${translatePixelsX}) + translateY(${translatePixelsY}) scale(${zoomPercentage}) - translateX(${translatePixelsX}) - translateY(${translatePixelsY})`, + rotate(${rotationDegrees})`, }; return ( From 776b0e8198e1179ee5f1b7ee9edb6e07d6c14ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:05:27 +0100 Subject: [PATCH 025/863] Change comment styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0db907003c..b6fa9ef35b 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -61,8 +61,8 @@ export default class ImageView extends React.Component { lastY = 0; componentDidMount() { - // We have to use addEventListener() because the listener - // needs to be passive in order to work with Chromium + /* We have to use addEventListener() because the listener + * needs to be passive in order to work with Chromium */ this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } From f0abd52130b6f70df4bef144d26afdda05819bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:09:01 +0100 Subject: [PATCH 026/863] Remove the need to press ctrl while zooming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index b6fa9ef35b..d40708af4c 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -79,23 +79,21 @@ export default class ImageView extends React.Component { }; onWheel = (ev) => { - if (ev.ctrlKey) { - ev.stopPropagation(); - ev.preventDefault(); - const newZoom =this.state.zoom - ev.deltaY; + ev.stopPropagation(); + ev.preventDefault(); + const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= 100) { - this.setState({ - zoom: 100, - translationX: 0, - translationY: 0, - }); - return; - } + if (newZoom <= 100) { this.setState({ - zoom: newZoom, + zoom: 100, + translationX: 0, + translationY: 0, }); + return; } + this.setState({ + zoom: newZoom, + }); } onRedactClick = () => { From cbfa6c5f94bc839ce158ee994a2efda81d024508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:15:25 +0100 Subject: [PATCH 027/863] Fix some sizing issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 0153d372fc..2c3d881394 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -39,7 +39,7 @@ limitations under the License. pointer-events: all; max-width: 100vw; - max-height: 90vh; + max-height: 80vh; min-width: 100px; min-height: 100px; From 61c5e7e8f17356c849fda70793cd6e5c86f4342c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:15:39 +0100 Subject: [PATCH 028/863] Reorder label items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d40708af4c..b343ec37c2 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -266,8 +266,8 @@ export default class ImageView extends React.Component {
{ this.getName() }
- { metadata } { sizeRes } + { metadata }
From 0dff150bb271985133b551c0b99639fc68461cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:18:21 +0100 Subject: [PATCH 029/863] Fix some more sizing issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 2c3d881394..1b38021267 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -39,7 +39,7 @@ limitations under the License. pointer-events: all; max-width: 100vw; - max-height: 80vh; + max-height: 70vh; min-width: 100px; min-height: 100px; From 096fb33397e29e65cf0ec5dea77eb99b8b8c690e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:19:32 +0100 Subject: [PATCH 030/863] Always allow moving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index b343ec37c2..12112db48a 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -161,8 +161,6 @@ export default class ImageView extends React.Component { ev.stopPropagation(); ev.preventDefault(); - if (this.state.zoom <= 100) return false; - this.setState({moving: true}); this.initX = ev.pageX - this.lastX; this.initY = ev.pageY - this.lastY; From 3d62138cbd67fa7beefa9770abf954a35c8286f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:28:19 +0100 Subject: [PATCH 031/863] Set max zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 12112db48a..82f3386e8d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -91,6 +91,11 @@ export default class ImageView extends React.Component { }); return; } + if (newZoom >= 300) { + this.setState({zoom: 300}); + return; + } + this.setState({ zoom: newZoom, }); @@ -138,6 +143,11 @@ export default class ImageView extends React.Component { }; onZoomInClick = () => { + if (this.state.zoom >= 300) { + this.setState({zoom: 300}); + return; + } + this.setState({ zoom: this.state.zoom + 10, }); From f771b7ac98764135691f64fbddefded6cdb06621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:37:31 +0100 Subject: [PATCH 032/863] Added zoom button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/zoom-white.svg | 59 ++++++++++++++++++++++ src/components/views/elements/ImageView.js | 33 +++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 res/img/zoom-white.svg diff --git a/res/img/zoom-white.svg b/res/img/zoom-white.svg new file mode 100644 index 0000000000..19379cb881 --- /dev/null +++ b/res/img/zoom-white.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 82f3386e8d..8552b2e381 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -59,6 +59,8 @@ export default class ImageView extends React.Component { initY = 0; lastX = 0; lastY = 0; + minZoom = 100; + maxZoom = 300; componentDidMount() { /* We have to use addEventListener() because the listener @@ -83,16 +85,16 @@ export default class ImageView extends React.Component { ev.preventDefault(); const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= 100) { + if (newZoom <= this.minZoom) { this.setState({ - zoom: 100, + zoom: this.minZoom, translationX: 0, translationY: 0, }); return; } - if (newZoom >= 300) { - this.setState({zoom: 300}); + if (newZoom >= this.maxZoom) { + this.setState({zoom: this.maxZoom}); return; } @@ -143,8 +145,8 @@ export default class ImageView extends React.Component { }; onZoomInClick = () => { - if (this.state.zoom >= 300) { - this.setState({zoom: 300}); + if (this.state.zoom >= this.maxZoom) { + this.setState({zoom: this.maxZoom}); return; } @@ -154,9 +156,9 @@ export default class ImageView extends React.Component { }; onZoomOutClick = () => { - if (this.state.zoom <= 100) { + if (this.state.zoom <= this.minZoom) { this.setState({ - zoom: 100, + zoom: this.minZoom, translationX: 0, translationY: 0, }); @@ -167,6 +169,18 @@ export default class ImageView extends React.Component { }); } + onZoomClick = () => { + if (this.state.zoom <= this.minZoom) { + this.setState({zoom: this.maxZoom}); + } else { + this.setState({ + zoom: this.minZoom, + translationX: 0, + translationY: 0, + }); + } + } + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -278,6 +292,9 @@ export default class ImageView extends React.Component { { metadata }
+ + { + { From 6315c8ecefae446a11eb9b031e35b524a1bfa9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:45:33 +0100 Subject: [PATCH 033/863] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 1b38021267..43333a25e6 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -59,7 +59,6 @@ limitations under the License. right: 0; padding: 50px 50px 0 0; display: flex; - } .mx_ImageView_label { From 00bc97b63fde4d1605b8820422d98fa73680e889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:45:47 +0100 Subject: [PATCH 034/863] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f8ef44763d..cd4285753f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1833,11 +1833,14 @@ "expand": "expand", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", + "Zoom": "Zoom", + "Zoom in": "Zoom in", + "Zoom out": "Zoom out", "Rotate Left": "Rotate Left", "Rotate counter-clockwise": "Rotate counter-clockwise", "Rotate Right": "Rotate Right", "Rotate clockwise": "Rotate clockwise", - "Download this file": "Download this file", + "Download": "Download", "Information": "Information", "Language Dropdown": "Language Dropdown", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", @@ -2593,7 +2596,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.", "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.", "Your recovery key": "Your recovery key", - "Download": "Download", "Your recovery key has been copied to your clipboard, paste it to:": "Your recovery key has been copied to your clipboard, paste it to:", "Your recovery key is in your Downloads folder.": "Your recovery key is in your Downloads folder.", "Print it and store it somewhere safe": "Print it and store it somewhere safe", From 78b3f50bfd4bbe3de053d7b7d994ad779e3002a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Sun, 20 Dec 2020 23:14:56 +0100 Subject: [PATCH 035/863] Use LaTeX delimiters by default, add /tex command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since parsing for $'s as maths delimiters is tricky, switch the default to \(...\) for inline and \[...\] for display maths as it is used in LaTeX. Add /tex command to explicitly parse in TeX mode, which uses $...$ for inline and $$...$$ for display maths. Signed-off-by: Sven Mäder --- src/SlashCommands.tsx | 18 +++++++ src/editor/deserialize.ts | 8 +-- src/editor/serialize.ts | 98 ++++++++++++++++++++++++++++--------- src/i18n/strings/en_EN.json | 1 + 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 79c21c4af5..e9bde933ec 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -48,6 +48,7 @@ import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; import {CHAT_EFFECTS} from "./effects" import CallHandler from "./CallHandler"; +import {markdownSerializeIfNeeded} from './editor/serialize'; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -223,6 +224,23 @@ export const Commands = [ }, category: CommandCategories.messages, }), + new Command({ + command: 'tex', + args: '', + description: _td('Sends a message in TeX mode, using $ and $$ delimiters for maths'), + runFn: function(roomId, args) { + if (SettingsStore.getValue("feature_latex_maths")) { + if (args) { + let html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); + return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); + } + return reject(this.getUsage()); + } else { + return reject("Render LaTeX maths in messages needs to be enabled in Labs"); + } + }, + category: CommandCategories.messages, + }), new Command({ command: 'ddg', args: '', diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index 6336b4c46b..a1ee079af5 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -136,11 +136,11 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl // math nodes are translated back into delimited latex strings if (n.hasAttribute("data-mx-maths")) { const delimLeft = (n.nodeName == "SPAN") ? - (SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "$" : - (SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "$$"; + (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'] || "$$"; + (SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "\\)" : + (SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "\\]"; const tex = n.getAttribute("data-mx-maths"); return partCreator.plain(delimLeft + tex + delimRight); } else if (!checkDescendInto(n)) { diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index c1f4da306b..ca798a324e 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -41,24 +41,57 @@ export function mdSerialize(model: EditorModel) { }, ""); } -export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - let md = mdSerialize(model); +export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, {forceTEX = false} = {}) { + // copy of raw input to remove unwanted math later + const orig = md; if (SettingsStore.getValue("feature_latex_maths")) { - const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "\\$\\$(([^$]|\\\\\\$)*)\\$\\$"; - const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "\\$(([^$]|\\\\\\$)*)\\$"; + if (forceTEX) { + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; - }); + // conditions for display math detection ($$...$$): + // - left delimiter ($$) is not escaped by a backslash + // - pattern starts at the beginning of a line + // - left delimiter is not followed by a space or tab character + // - pattern ends at the end of a line + const displayPattern = "^(?!\\\\)\\$\\$(?![ \\t])(([^$]|\\\\\\$)+?)\\$\\$$"; - md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { - const p1e = AllHtmlEntities.encode(p1); - return ``; - }); + // conditions for inline math detection ($...$): + // - left and right delimiters ($) are not escaped by backslashes + // - pattern starts at the beginning of a line or follows a whitespace character + // - left delimiter is not followed by a whitespace character + // - right delimiter is not preseeded by a whitespace character + const inlinePattern = "(^|\\s)(?!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)*[^\\\\\\s\\$](?:\\\\\\$)?)\\$"; + + md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { + const p1e = AllHtmlEntities.encode(p1); + return `
\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + } else { + // detect math with latex delimiters, inline: \(...\), display \[...\] + const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || + "^\\\\\\[(.*?)\\\\\\]$"; + const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || + "(^|\\s)\\\\\\((.*?)\\\\\\)"; + + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1) { + const p1e = AllHtmlEntities.encode(p1); + return `
\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + } // make sure div tags always start on a new line, otherwise it will confuse // the markdown parser @@ -69,15 +102,30 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = if (!parser.isPlainText() || forceHTML) { // feed Markdown output to HTML parser const phtml = cheerio.load(parser.toHTML(), - { _useHtmlParser2: true, decodeEntities: false }) + { _useHtmlParser2: true, decodeEntities: false }); - // add fallback output for latex math, which should not be interpreted as markdown - phtml('div, span').each(function(i, e) { - const tex = phtml(e).attr('data-mx-maths') - if (tex) { - phtml(e).html(`${tex}`) - } - }); + if (SettingsStore.getValue("feature_latex_maths")) { + // original Markdown without LaTeX replacements + const parserOrig = new Markdown(orig); + const phtmlOrig = cheerio.load(parserOrig.toHTML(), + { _useHtmlParser2: true, decodeEntities: false }); + + // since maths delimiters are handled before Markdown, + // code blocks could contain mangled content. + // replace code blocks with original content + phtml('code').contents('div, span').each(function(i) { + const origData = phtmlOrig('code').contents('div, span')[i].data; + phtml('code').contents('div, span')[i].data = origData; + }); + + // add fallback output for latex math, which should not be interpreted as markdown + phtml('div, span').each(function(i, e) { + const tex = phtml(e).attr('data-mx-maths') + if (tex) { + phtml(e).html(`${tex}`) + } + }); + } return phtml.html(); } // ensure removal of escape backslashes in non-Markdown messages @@ -86,6 +134,12 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = } } +export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { + let md = mdSerialize(model); + + return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); +} + export function textSerialize(model: EditorModel) { return model.parts.reduce((text, part) => { switch (part.type) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2fb70ecdb1..bf6c61414a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,6 +416,7 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", + "Sends a message in TeX mode, using $ and $$ delimiters for maths": "Sends a message in TeX mode, using $ and $$ delimiters for maths", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From 997e08f21e32d61f164cfefb4c397fc48d19de71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 21 Dec 2020 10:07:46 +0100 Subject: [PATCH 036/863] Reorganize buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8552b2e381..4e38b4b4eb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -292,15 +292,16 @@ export default class ImageView extends React.Component { { metadata }
+ { redactButton } { - - { - { + + { + { @@ -310,7 +311,6 @@ export default class ImageView extends React.Component { { - { redactButton } { From ad5844fcc0b9e585e0f0f5766a33a9e7f716d43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 21 Dec 2020 20:33:03 +0100 Subject: [PATCH 037/863] Fix i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- 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 32c3a61f8a..a3345caac5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1839,8 +1839,8 @@ "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", "Zoom": "Zoom", - "Zoom in": "Zoom in", "Zoom out": "Zoom out", + "Zoom in": "Zoom in", "Rotate Left": "Rotate Left", "Rotate counter-clockwise": "Rotate counter-clockwise", "Rotate Right": "Rotate Right", From fb57123e25966912277ac65927304b088b9e7db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Tue, 22 Dec 2020 12:18:38 +0100 Subject: [PATCH 038/863] Improve inline latex regex matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Mäder --- src/SlashCommands.tsx | 2 +- src/editor/serialize.ts | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index e9bde933ec..bf190fc450 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -231,7 +231,7 @@ export const Commands = [ runFn: function(roomId, args) { if (SettingsStore.getValue("feature_latex_maths")) { if (args) { - let html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); + const html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); } return reject(this.getUsage()); diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index ca798a324e..4ef722e334 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -52,18 +52,18 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, // const displayPattern = "^(? Date: Tue, 22 Dec 2020 13:31:58 +0100 Subject: [PATCH 039/863] Fix linting error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Mäder --- src/editor/serialize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 4ef722e334..3aafa70fe8 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -144,7 +144,7 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, } export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - let md = mdSerialize(model); + const md = mdSerialize(model); return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); } From 5de92b68d954ba3f997f2d5713d954ee05303b2e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 27 Jan 2021 11:39:57 +0000 Subject: [PATCH 040/863] Show a specific error for hs_disabled --- src/components/structures/LoggedInView.tsx | 2 +- src/components/structures/RoomStatusBar.js | 4 ++++ src/components/structures/auth/Login.tsx | 3 +++ src/components/structures/auth/Registration.tsx | 1 + src/toasts/ServerLimitToast.tsx | 1 + src/utils/ErrorUtils.js | 1 + 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 70ec2b7033..508b7f05e7 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -94,7 +94,7 @@ interface IProps { interface IUsageLimit { // eslint-disable-next-line camelcase - limit_type: "monthly_active_user" | string; + limit_type: "monthly_active_user" | "hs_disabled" | string; // eslint-disable-next-line camelcase admin_contact?: string; } diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index c1c4ad6292..aa4bceba74 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -195,6 +195,10 @@ export default class RoomStatusBar extends React.Component { "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " + "Please contact your service administrator to continue using the service.", ), + 'hs_disabled': _td( + "Your message wasn't sent because this homeserver has been blocked by it's administrator. " + + "Please contact your service administrator to continue using the service.", + ), '': _td( "Your message wasn't sent because this homeserver has exceeded a resource limit. " + "Please contact your service administrator to continue using the service.", diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 606aeb44ab..a9fd363763 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -218,6 +218,9 @@ export default class LoginComponent extends React.PureComponent 'monthly_active_user': _td( "This homeserver has hit its Monthly Active User limit.", ), + 'hs_blocked': _td( + "This homeserver has been blocked by it's administrator.", + ), '': _td( "This homeserver has exceeded one of its resource limits.", ), diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 095f3d3433..f9d338902c 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -276,6 +276,7 @@ export default class Registration extends React.Component { response.data.admin_contact, { 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), + 'hs_blocked': _td("This homeserver has been blocked by it's administrator."), '': _td("This homeserver has exceeded one of its resource limits."), }, ); diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx index d35140be3d..9dbe8c05f1 100644 --- a/src/toasts/ServerLimitToast.tsx +++ b/src/toasts/ServerLimitToast.tsx @@ -26,6 +26,7 @@ const TOAST_KEY = "serverlimit"; export const showToast = (limitType: string, adminContact?: string, syncError?: boolean) => { const errorText = messageForResourceLimitError(limitType, adminContact, { 'monthly_active_user': _td("Your homeserver has exceeded its user limit."), + 'hs_blocked': _td("This homeserver has been blocked by it's administrator."), '': _td("Your homeserver has exceeded one of its resource limits."), }); const contactText = messageForResourceLimitError(limitType, adminContact, { diff --git a/src/utils/ErrorUtils.js b/src/utils/ErrorUtils.js index f0a4d7c49e..2c6acd5503 100644 --- a/src/utils/ErrorUtils.js +++ b/src/utils/ErrorUtils.js @@ -62,6 +62,7 @@ export function messageForSyncError(err) { err.data.admin_contact, { 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), + 'hs_blocked': _td("This homeserver has been blocked by its administrator."), '': _td("This homeserver has exceeded one of its resource limits."), }, ); From 27724a93d28d7945e49f94b5fa1158095bd84d8d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 27 Jan 2021 11:42:36 +0000 Subject: [PATCH 041/863] new strings --- src/i18n/strings/en_EN.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8d047ea3f1..e55ab581ca 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -650,6 +650,7 @@ "Unexpected error resolving identity server configuration": "Unexpected error resolving identity server configuration", "The message you are trying to send is too large.": "The message you are trying to send is too large.", "This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.", + "This homeserver has been blocked by its administrator.": "This homeserver has been blocked by its administrator.", "This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.", "Please contact your service administrator to continue using the service.": "Please contact your service administrator to continue using the service.", "Unable to connect to Homeserver. Retrying...": "Unable to connect to Homeserver. Retrying...", @@ -727,6 +728,7 @@ "Enable desktop notifications": "Enable desktop notifications", "Enable": "Enable", "Your homeserver has exceeded its user limit.": "Your homeserver has exceeded its user limit.", + "This homeserver has been blocked by it's administrator.": "This homeserver has been blocked by it's administrator.", "Your homeserver has exceeded one of its resource limits.": "Your homeserver has exceeded one of its resource limits.", "Contact your server admin.": "Contact your server admin.", "Warning": "Warning", @@ -2471,6 +2473,7 @@ "Filter rooms and people": "Filter rooms and people", "You can't send any messages until you review and agree to our terms and conditions.": "You can't send any messages until you review and agree to our terms and conditions.", "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.", + "Your message wasn't sent because this homeserver has been blocked by it's administrator. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has been blocked by it's administrator. Please contact your service administrator to continue using the service.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.", "%(count)s of your messages have not been sent.|other": "Some of your messages have not been sent.", "%(count)s of your messages have not been sent.|one": "Your message was not sent.", From c4f0987487c58ed88dacc62ff155dfa140729fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 00:11:06 +0100 Subject: [PATCH 042/863] Use TeX and LaTeX delimiters by default for serialize --- src/SlashCommands.tsx | 2 +- src/editor/serialize.ts | 71 +++++++++++++++++++++---------------- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index bf190fc450..387b9991db 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -227,7 +227,7 @@ export const Commands = [ new Command({ command: 'tex', args: '', - description: _td('Sends a message in TeX mode, using $ and $$ delimiters for maths'), + description: _td('Sends a message in TeX mode, without restrictions'), runFn: function(roomId, args) { if (SettingsStore.getValue("feature_latex_maths")) { if (args) { diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 3aafa70fe8..61d7878de5 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -47,28 +47,12 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, if (SettingsStore.getValue("feature_latex_maths")) { if (forceTEX) { - // detect math with tex delimiters, inline: $...$, display $$...$$ - // preferably use negative lookbehinds, not supported in all major browsers: - // const displayPattern = "^(?\n\n
\n\n`; + md = md.replace(RegExp(displayPattern, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; }); md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { @@ -76,24 +60,51 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, return `${p1}`; }); } else { + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + // detect math with latex delimiters, inline: \(...\), display \[...\] // conditions for display math detection \[...\]: - // - pattern starts at beginning of line - // - pattern ends at end of line + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "^\\\\\\[(.*?)\\\\\\]$"; + "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; // conditions for inline math detection \(...\): // - pattern starts at beginning of line or is not prefixed with backslash - // (this allows escaping and requires that multiple consecutive - // patterns are separated by at least one character) + // - pattern is not empty const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "(^|[^\\\\])\\\\\\((.*?)\\\\\\)"; + "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - md = md.replace(RegExp(displayPattern, "gms"), function(m, p1) { - const p1e = AllHtmlEntities.encode(p1); - return `
\n\n
\n\n`; + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; }); md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bf6c61414a..bcae343550 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,7 +416,7 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", - "Sends a message in TeX mode, using $ and $$ delimiters for maths": "Sends a message in TeX mode, using $ and $$ delimiters for maths", + "Sends a message in TeX mode, without restrictions": "Sends a message in TeX mode, without restrictions", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From bcc069771061b7559313ded1f453f91eaf0f283f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 13:05:49 +0100 Subject: [PATCH 043/863] Remove /tex command --- src/SlashCommands.tsx | 18 ------ src/editor/serialize.ts | 108 +++++++++++++++--------------------- src/i18n/strings/en_EN.json | 1 - 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 387b9991db..79c21c4af5 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -48,7 +48,6 @@ import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; import {CHAT_EFFECTS} from "./effects" import CallHandler from "./CallHandler"; -import {markdownSerializeIfNeeded} from './editor/serialize'; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -224,23 +223,6 @@ export const Commands = [ }, category: CommandCategories.messages, }), - new Command({ - command: 'tex', - args: '', - description: _td('Sends a message in TeX mode, without restrictions'), - runFn: function(roomId, args) { - if (SettingsStore.getValue("feature_latex_maths")) { - if (args) { - const html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); - } - return reject(this.getUsage()); - } else { - return reject("Render LaTeX maths in messages needs to be enabled in Labs"); - } - }, - category: CommandCategories.messages, - }), new Command({ command: 'ddg', args: '', diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 61d7878de5..6b1d97a0ef 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -41,77 +41,63 @@ export function mdSerialize(model: EditorModel) { }, ""); } -export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, {forceTEX = false} = {}) { +export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { + let md = mdSerialize(model); // copy of raw input to remove unwanted math later const orig = md; if (SettingsStore.getValue("feature_latex_maths")) { - if (forceTEX) { - const displayPattern = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; - const inlinePattern = "(^|[^\\\\$])\\$(([^$]|\\\\\\$)*([^\\\\\\$]|\\\\\\$))\\$"; + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; - }); + // conditions for display math detection $$...$$: + // - pattern starts at beginning of line or is not prefixed with backslash or dollar + // - left delimiter ($$) is not escaped by backslash + const displayPatternDollar = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; - md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); - } else { - // detect math with tex delimiters, inline: $...$, display $$...$$ - // preferably use negative lookbehinds, not supported in all major browsers: - // const displayPattern = "^(?\n\n
\n\n`; + }); - // conditions for inline math detection $...$: - // - pattern starts at beginning of line, follows whitespace character or punctuation - // - pattern is on a single line - // - left and right delimiters ($) are not escaped by backslashes - // - left delimiter is not followed by whitespace character - // - right delimiter is not prefixed with whitespace character - const inlinePatternDollar = "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; + md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); - md = md.replace(RegExp(displayPatternDollar, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}
\n\n
\n\n`; - }); + // detect math with latex delimiters, inline: \(...\), display \[...\] - md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); + // conditions for display math detection \[...\]: + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty + const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || + "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; - // detect math with latex delimiters, inline: \(...\), display \[...\] + // conditions for inline math detection \(...\): + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty + const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || + "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - // conditions for display math detection \[...\]: - // - pattern starts at beginning of line or is not prefixed with backslash - // - pattern is not empty - const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; + }); - // conditions for inline math detection \(...\): - // - pattern starts at beginning of line or is not prefixed with backslash - // - pattern is not empty - const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - - md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}
\n\n
\n\n`; - }); - - md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); - } + md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); // make sure div tags always start on a new line, otherwise it will confuse // the markdown parser @@ -154,12 +140,6 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, } } -export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - const md = mdSerialize(model); - - return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); -} - export function textSerialize(model: EditorModel) { return model.parts.reduce((text, part) => { switch (part.type) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bcae343550..2fb70ecdb1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,7 +416,6 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", - "Sends a message in TeX mode, without restrictions": "Sends a message in TeX mode, without restrictions", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From ac1f9b4247f7eefacc969bfcbdbf30984c8e15e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 15:49:20 +0100 Subject: [PATCH 044/863] Add config keys for alternative patterns --- src/editor/serialize.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 6b1d97a0ef..6655c64347 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -55,7 +55,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = // conditions for display math detection $$...$$: // - pattern starts at beginning of line or is not prefixed with backslash or dollar // - left delimiter ($$) is not escaped by backslash - const displayPatternDollar = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; + const displayPatternAlternative = (SdkConfig.get()['latex_maths_delims'] || + {})['display_pattern_alternative'] || + "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; // conditions for inline math detection $...$: // - pattern starts at beginning of line, follows whitespace character or punctuation @@ -63,14 +65,16 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = // - left and right delimiters ($) are not escaped by backslashes // - left delimiter is not followed by whitespace character // - right delimiter is not prefixed with whitespace character - const inlinePatternDollar = "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; + const inlinePatternAlternative = (SdkConfig.get()['latex_maths_delims'] || + {})['inline_pattern_alternative'] || + "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; - md = md.replace(RegExp(displayPatternDollar, "gm"), function(m, p1, p2) { + md = md.replace(RegExp(displayPatternAlternative, "gm"), function(m, p1, p2) { const p2e = AllHtmlEntities.encode(p2); return `${p1}
\n\n
\n\n`; }); - md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + md = md.replace(RegExp(inlinePatternAlternative, "gm"), function(m, p1, p2) { const p2e = AllHtmlEntities.encode(p2); return `${p1}`; }); From cc38bcf333bc9fdd7d8ebc7d0b4d06330ba7e359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 6 Feb 2021 15:09:21 +0100 Subject: [PATCH 045/863] Display room name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/Pill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index daa4cb70e2..f1527c48b1 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -226,7 +226,7 @@ class Pill extends React.Component { case Pill.TYPE_ROOM_MENTION: { const room = this.state.room; if (room) { - linkText = resource; + linkText = room.name; if (this.props.shouldShowPillAvatar) { avatar =
); } - } else if (this.state.view === Views.WELCOME) { + } else if (this.state.view === Views.WELCOME && !shouldUseLoginForWelcome(SdkConfig.get())) { const Welcome = sdk.getComponent('auth.Welcome'); view = ; } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { @@ -2020,7 +2021,8 @@ export default class MatrixChat extends React.PureComponent { {...this.getServerProperties()} /> ); - } else if (this.state.view === Views.LOGIN) { + } else if (this.state.view === Views.LOGIN + || (this.state.view === Views.WELCOME && shouldUseLoginForWelcome(SdkConfig.get()))) { const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); const Login = sdk.getComponent('structures.auth.Login'); view = ( diff --git a/src/utils/pages.js b/src/utils/pages.ts similarity index 68% rename from src/utils/pages.js rename to src/utils/pages.ts index d63ca3f2c7..bae76be29d 100644 --- a/src/utils/pages.js +++ b/src/utils/pages.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 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. @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -export function getHomePageUrl(appConfig) { +import { ConfigOptions } from "../SdkConfig"; + +export function getHomePageUrl(appConfig: ConfigOptions): string | null { const pagesConfig = appConfig.embeddedPages; - let pageUrl = null; - if (pagesConfig) { - pageUrl = pagesConfig.homeUrl; - } + let pageUrl = pagesConfig?.homeUrl; + if (!pageUrl) { // This is a deprecated config option for the home page // (despite the name, given we also now have a welcome @@ -29,3 +29,8 @@ export function getHomePageUrl(appConfig) { return pageUrl; } + +export function shouldUseLoginForWelcome(appConfig: ConfigOptions): boolean { + const pagesConfig = appConfig.embeddedPages; + return pagesConfig?.loginForWelcome === true; +} From 54c38844d254546a35afbe8d81be4f6380a54262 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Wed, 17 Feb 2021 22:00:48 +1300 Subject: [PATCH 062/863] Use key bindings in BasicMessageComposer --- src/KeyBindingsManager.ts | 164 +++++++++++++++- .../views/rooms/BasicMessageComposer.tsx | 175 +++++++++--------- 2 files changed, 245 insertions(+), 94 deletions(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index e8f4126fbd..ef5084c16c 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -4,6 +4,8 @@ import SettingsStore from './settings/SettingsStore'; export enum KeyBindingContext { /** Key bindings for the chat message composer component */ MessageComposer = 'MessageComposer', + /** Key bindings for text editing autocompletion */ + AutoComplete = 'AutoComplete', } export enum KeyAction { @@ -21,9 +23,34 @@ export enum KeyAction { EditPrevMessage = 'EditPrevMessage', /** Start editing the user's next sent message */ EditNextMessage = 'EditNextMessage', - - /** Cancel editing a message or cancel replying to a message */ + /** Cancel editing a message or cancel replying to a message*/ CancelEditing = 'CancelEditing', + + /** Set bold format the current selection */ + FormatBold = 'FormatBold', + /** Set italics format the current selection */ + FormatItalics = 'FormatItalics', + /** Format the current selection as quote */ + FormatQuote = 'FormatQuote', + /** Undo the last editing */ + EditUndo = 'EditUndo', + /** Redo editing */ + EditRedo = 'EditRedo', + /** Insert new line */ + NewLine = 'NewLine', + MoveCursorToStart = 'MoveCursorToStart', + MoveCursorToEnd = 'MoveCursorToEnd', + + // Autocomplete + + /** Apply the current autocomplete selection */ + AutocompleteApply = 'AutocompleteApply', + /** Cancel autocompletion */ + AutocompleteCancel = 'AutocompleteCancel', + /** Move to the previous autocomplete selection */ + AutocompletePrevSelection = 'AutocompletePrevSelection', + /** Move to the next autocomplete selection */ + AutocompleteNextSelection = 'AutocompleteNextSelection', } /** @@ -84,7 +111,69 @@ const messageComposerBindings = (): KeyBinding[] => { key: Key.ESCAPE, }, }, + { + action: KeyAction.FormatBold, + keyCombo: { + key: Key.B, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.FormatItalics, + keyCombo: { + key: Key.I, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.FormatQuote, + keyCombo: { + key: Key.GREATER_THAN, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: KeyAction.EditUndo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + }, + }, + // Note: the following two bindings also work with just HOME and END, add them here? + { + action: KeyAction.MoveCursorToStart, + keyCombo: { + key: Key.HOME, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.MoveCursorToEnd, + keyCombo: { + key: Key.END, + ctrlOrCmd: true, + }, + }, ]; + if (isMac) { + bindings.push({ + action: KeyAction.EditRedo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + shiftKey: true, + }, + }); + } else { + bindings.push({ + action: KeyAction.EditRedo, + keyCombo: { + key: Key.Y, + ctrlOrCmd: true, + }, + }); + } if (SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend')) { bindings.push({ action: KeyAction.Send, @@ -93,6 +182,12 @@ const messageComposerBindings = (): KeyBinding[] => { ctrlOrCmd: true, }, }); + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + }, + }); } else { bindings.push({ action: KeyAction.Send, @@ -100,17 +195,75 @@ const messageComposerBindings = (): KeyBinding[] => { key: Key.ENTER, }, }); + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + shiftKey: true, + }, + }); + if (isMac) { + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + altKey: true, + }, + }); + } } - return bindings; } +const autocompleteBindings = (): KeyBinding[] => { + return [ + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + }, + }, + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + ctrlKey: true, + }, + }, + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + shiftKey: true, + }, + }, + { + action: KeyAction.AutocompleteCancel, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: KeyAction.AutocompletePrevSelection, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: KeyAction.AutocompleteNextSelection, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + ] +} + /** * Helper method to check if a KeyboardEvent matches a KeyCombo * * Note, this method is only exported for testing. */ -export function isKeyComboMatch(ev: KeyboardEvent, combo: KeyCombo, onMac: boolean): boolean { +export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo: KeyCombo, onMac: boolean): boolean { if (combo.key !== undefined && ev.key !== combo.key) { return false; } @@ -160,12 +313,13 @@ export class KeyBindingsManager { */ contextBindings: Record = { [KeyBindingContext.MessageComposer]: messageComposerBindings, + [KeyBindingContext.AutoComplete]: autocompleteBindings, }; /** * Finds a matching KeyAction for a given KeyboardEvent */ - getAction(context: KeyBindingContext, ev: KeyboardEvent): KeyAction { + getAction(context: KeyBindingContext, ev: KeyboardEvent | React.KeyboardEvent): KeyAction { const bindings = this.contextBindings[context]?.(); if (!bindings) { return KeyAction.None; diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 017ce77166..d0119ddc05 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -46,6 +46,7 @@ import {IDiff} from "../../../editor/diff"; import AutocompleteWrapperModel from "../../../editor/autocomplete"; import DocumentPosition from "../../../editor/position"; import {ICompletion} from "../../../autocomplete/Autocompleter"; +import { getKeyBindingsManager, KeyBindingContext, KeyAction } from '../../../KeyBindingsManager'; // matches emoticons which follow the start of a line or whitespace const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); @@ -419,98 +420,94 @@ export default class BasicMessageEditor extends React.Component private onKeyDown = (event: React.KeyboardEvent) => { const model = this.props.model; - const modKey = IS_MAC ? event.metaKey : event.ctrlKey; let handled = false; - // format bold - if (modKey && event.key === Key.B) { - this.onFormatAction(Formatting.Bold); - handled = true; - // format italics - } else if (modKey && event.key === Key.I) { - this.onFormatAction(Formatting.Italics); - handled = true; - // format quote - } else if (modKey && event.key === Key.GREATER_THAN) { - this.onFormatAction(Formatting.Quote); - handled = true; - // redo - } else if ((!IS_MAC && modKey && event.key === Key.Y) || - (IS_MAC && modKey && event.shiftKey && event.key === Key.Z)) { - if (this.historyManager.canRedo()) { - const {parts, caret} = this.historyManager.redo(); - // pass matching inputType so historyManager doesn't push echo - // when invoked from rerender callback. - model.reset(parts, caret, "historyRedo"); - } - handled = true; - // undo - } else if (modKey && event.key === Key.Z) { - if (this.historyManager.canUndo()) { - const {parts, caret} = this.historyManager.undo(this.props.model); - // pass matching inputType so historyManager doesn't push echo - // when invoked from rerender callback. - model.reset(parts, caret, "historyUndo"); - } - handled = true; - // insert newline on Shift+Enter - } else if (event.key === Key.ENTER && (event.shiftKey || (IS_MAC && event.altKey))) { - this.insertText("\n"); - handled = true; - // move selection to start of composer - } else if (modKey && event.key === Key.HOME && !event.shiftKey) { - setSelection(this.editorRef.current, model, { - index: 0, - offset: 0, - }); - handled = true; - // move selection to end of composer - } else if (modKey && event.key === Key.END && !event.shiftKey) { - setSelection(this.editorRef.current, model, { - index: model.parts.length - 1, - offset: model.parts[model.parts.length - 1].text.length, - }); - handled = true; - // autocomplete or enter to send below shouldn't have any modifier keys pressed. - } else { - const metaOrAltPressed = event.metaKey || event.altKey; - const modifierPressed = metaOrAltPressed || event.shiftKey; - if (model.autoComplete && model.autoComplete.hasCompletions()) { - const autoComplete = model.autoComplete; - switch (event.key) { - case Key.ARROW_UP: - if (!modifierPressed) { - autoComplete.onUpArrow(event); - handled = true; - } - break; - case Key.ARROW_DOWN: - if (!modifierPressed) { - autoComplete.onDownArrow(event); - handled = true; - } - break; - case Key.TAB: - if (!metaOrAltPressed) { - autoComplete.onTab(event); - handled = true; - } - break; - case Key.ESCAPE: - if (!modifierPressed) { - autoComplete.onEscape(event); - handled = true; - } - break; - default: - return; // don't preventDefault on anything else - } - } else if (event.key === Key.TAB) { - this.tabCompleteName(event); + const action = getKeyBindingsManager().getAction(KeyBindingContext.MessageComposer, event); + switch (action) { + case KeyAction.FormatBold: + this.onFormatAction(Formatting.Bold); handled = true; - } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { - this.formatBarRef.current.hide(); - } + break; + case KeyAction.FormatItalics: + this.onFormatAction(Formatting.Italics); + handled = true; + break; + case KeyAction.FormatQuote: + this.onFormatAction(Formatting.Quote); + handled = true; + break; + case KeyAction.EditRedo: + if (this.historyManager.canRedo()) { + const {parts, caret} = this.historyManager.redo(); + // pass matching inputType so historyManager doesn't push echo + // when invoked from rerender callback. + model.reset(parts, caret, "historyRedo"); + } + handled = true; + break; + case KeyAction.EditUndo: + if (this.historyManager.canUndo()) { + const {parts, caret} = this.historyManager.undo(this.props.model); + // pass matching inputType so historyManager doesn't push echo + // when invoked from rerender callback. + model.reset(parts, caret, "historyUndo"); + } + handled = true; + break; + case KeyAction.NewLine: + this.insertText("\n"); + handled = true; + break; + case KeyAction.MoveCursorToStart: + setSelection(this.editorRef.current, model, { + index: 0, + offset: 0, + }); + handled = true; + break; + case KeyAction.MoveCursorToEnd: + setSelection(this.editorRef.current, model, { + index: model.parts.length - 1, + offset: model.parts[model.parts.length - 1].text.length, + }); + handled = true; + break; } + if (handled) { + event.preventDefault(); + event.stopPropagation(); + return; + } + + const autocompleteAction = getKeyBindingsManager().getAction(KeyBindingContext.AutoComplete, event); + if (model.autoComplete && model.autoComplete.hasCompletions()) { + const autoComplete = model.autoComplete; + switch (autocompleteAction) { + case KeyAction.AutocompletePrevSelection: + autoComplete.onUpArrow(event); + handled = true; + break; + case KeyAction.AutocompleteNextSelection: + autoComplete.onDownArrow(event); + handled = true; + break; + case KeyAction.AutocompleteApply: + autoComplete.onTab(event); + handled = true; + break; + case KeyAction.AutocompleteCancel: + autoComplete.onEscape(event); + handled = true; + break; + default: + return; // don't preventDefault on anything else + } + } else if (autocompleteAction === KeyAction.AutocompleteApply) { + this.tabCompleteName(event); + handled = true; + } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { + this.formatBarRef.current.hide(); + } + if (handled) { event.preventDefault(); event.stopPropagation(); From e5d68142c6e35811a37d2ea709167ead99247a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 20:47:42 +0100 Subject: [PATCH 063/863] Remove zoom icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/zoom-white.svg | 59 ---------------------- src/components/views/elements/ImageView.js | 3 -- 2 files changed, 62 deletions(-) delete mode 100644 res/img/zoom-white.svg diff --git a/res/img/zoom-white.svg b/res/img/zoom-white.svg deleted file mode 100644 index 19379cb881..0000000000 --- a/res/img/zoom-white.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 4e38b4b4eb..be849d1dde 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -293,9 +293,6 @@ export default class ImageView extends React.Component {
{ redactButton } - - { - { From 35663c35d27e6fc6211ad501375c49a1b8c469d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 20:49:31 +0100 Subject: [PATCH 064/863] Reorder the icons according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index be849d1dde..7c40604a88 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -293,18 +293,18 @@ export default class ImageView extends React.Component {
{ redactButton } + + { + + + { + { { - - { - - - { - { From 6cf19e889777a1732182274ae0408dc8a746d7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 21:04:21 +0100 Subject: [PATCH 065/863] Update icons according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/cancel-white.svg | 10 --- res/img/download-white.svg | 95 ---------------------- res/img/image-view/close.svg | 4 + res/img/image-view/download.svg | 3 + res/img/image-view/rotate-ccw.svg | 3 + res/img/image-view/rotate-cw.svg | 3 + res/img/image-view/zoom-in.svg | 3 + res/img/image-view/zoom-out.svg | 3 + res/img/minus-white.svg | 64 --------------- res/img/plus-white.svg | 73 ----------------- res/img/rotate-ccw.svg | 1 - res/img/rotate-cw.svg | 1 - src/components/views/elements/ImageView.js | 12 +-- 13 files changed, 25 insertions(+), 250 deletions(-) delete mode 100644 res/img/cancel-white.svg delete mode 100644 res/img/download-white.svg create mode 100644 res/img/image-view/close.svg create mode 100644 res/img/image-view/download.svg create mode 100644 res/img/image-view/rotate-ccw.svg create mode 100644 res/img/image-view/rotate-cw.svg create mode 100644 res/img/image-view/zoom-in.svg create mode 100644 res/img/image-view/zoom-out.svg delete mode 100644 res/img/minus-white.svg delete mode 100644 res/img/plus-white.svg delete mode 100644 res/img/rotate-ccw.svg delete mode 100644 res/img/rotate-cw.svg diff --git a/res/img/cancel-white.svg b/res/img/cancel-white.svg deleted file mode 100644 index 65e14c2fbc..0000000000 --- a/res/img/cancel-white.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - Slice 1 - Created with Sketch. - - - - - \ No newline at end of file diff --git a/res/img/download-white.svg b/res/img/download-white.svg deleted file mode 100644 index 5c800b350e..0000000000 --- a/res/img/download-white.svg +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - - - - - - - - - - - diff --git a/res/img/image-view/close.svg b/res/img/image-view/close.svg new file mode 100644 index 0000000000..f849425264 --- /dev/null +++ b/res/img/image-view/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/image-view/download.svg b/res/img/image-view/download.svg new file mode 100644 index 0000000000..c51deed876 --- /dev/null +++ b/res/img/image-view/download.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/rotate-ccw.svg b/res/img/image-view/rotate-ccw.svg new file mode 100644 index 0000000000..85ea3198de --- /dev/null +++ b/res/img/image-view/rotate-ccw.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/rotate-cw.svg b/res/img/image-view/rotate-cw.svg new file mode 100644 index 0000000000..e337f3420e --- /dev/null +++ b/res/img/image-view/rotate-cw.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/zoom-in.svg b/res/img/image-view/zoom-in.svg new file mode 100644 index 0000000000..c0816d489e --- /dev/null +++ b/res/img/image-view/zoom-in.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/zoom-out.svg b/res/img/image-view/zoom-out.svg new file mode 100644 index 0000000000..0539e8c81a --- /dev/null +++ b/res/img/image-view/zoom-out.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/minus-white.svg b/res/img/minus-white.svg deleted file mode 100644 index 2921f34980..0000000000 --- a/res/img/minus-white.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - diff --git a/res/img/plus-white.svg b/res/img/plus-white.svg deleted file mode 100644 index 7759ace50a..0000000000 --- a/res/img/plus-white.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - - diff --git a/res/img/rotate-ccw.svg b/res/img/rotate-ccw.svg deleted file mode 100644 index 3924eca040..0000000000 --- a/res/img/rotate-ccw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/res/img/rotate-cw.svg b/res/img/rotate-cw.svg deleted file mode 100644 index 91021c96d8..0000000000 --- a/res/img/rotate-cw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 7c40604a88..10e5b083da 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -294,22 +294,22 @@ export default class ImageView extends React.Component {
{ redactButton } - { + { - { + { - { + { - { + { - { + { - { + {
Date: Wed, 24 Feb 2021 07:47:59 +0100 Subject: [PATCH 066/863] Reorganize elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 48 +++++++++++----------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 10e5b083da..d37f6ee618 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -284,33 +284,35 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
+
+
{ this.getName() }
- { sizeRes } + { sizeRes } { metadata } -
-
- { redactButton } - - { - - - { - - - { - - - { - - - { - - - { - +
+
+ { redactButton } + + { + + + { + + + { + + + { + + + { + + + { + +
Date: Wed, 24 Feb 2021 07:50:10 +0100 Subject: [PATCH 067/863] Remove name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d37f6ee618..3007fe566b 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -124,14 +124,6 @@ export default class ImageView extends React.Component { }); }; - getName() { - let name = this.props.name; - if (name && this.props.link) { - name = { name }; - } - return name; - } - onRotateCounterClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; @@ -286,11 +278,8 @@ export default class ImageView extends React.Component {
-
- { this.getName() } -
- { sizeRes } - { metadata } + { sizeRes } + { metadata }
{ redactButton } From 497131874b2c16e3d532558557bec59057ade65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 07:53:33 +0100 Subject: [PATCH 068/863] Remove size info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 3007fe566b..e3dbe2a411 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -20,7 +20,6 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; -import filesize from "filesize"; import AccessibleButton from "./AccessibleButton"; import Modal from "../../../Modal"; import * as sdk from "../../../index"; @@ -204,23 +203,6 @@ export default class ImageView extends React.Component { let mayRedact = false; const showEventMeta = !!this.props.mxEvent; - let res; - if (this.props.width && this.props.height) { - res = this.props.width + "x" + this.props.height + "px"; - } - - let size; - if (this.props.fileSize) { - size = filesize(this.props.fileSize); - } - - let sizeRes; - if (size && res) { - sizeRes = size + ", " + res; - } else { - sizeRes = size || res; - } - let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid @@ -278,7 +260,6 @@ export default class ImageView extends React.Component {
- { sizeRes } { metadata }
From 768d26818965d10646e3605c70cabaf7e596f3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 07:54:36 +0100 Subject: [PATCH 069/863] Fix css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 43333a25e6..ee02a1dce8 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -53,6 +53,9 @@ limitations under the License. z-index: 1000; align-self: flex-start; pointer-events: all; + display: flex; + justify-content: space-between; + width: 100%; } .mx_ImageView_toolbar { @@ -76,17 +79,7 @@ limitations under the License. padding: 5px; } -.mx_ImageView_name { - font-size: $font-18px; - margin-bottom: 6px; - word-wrap: break-word; -} - .mx_ImageView_metadata { font-size: $font-15px; opacity: 0.5; } - -.mx_ImageView_size { - font-size: $font-11px; -} From ab79deb88fa828ef1a13a1fecb9e0648b38b68f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 08:11:53 +0100 Subject: [PATCH 070/863] Update the looks a bit more MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 14 ++++++-- src/components/views/elements/ImageView.js | 38 ++++++++++++---------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ee02a1dce8..8afd2670a7 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -49,24 +49,32 @@ limitations under the License. } .mx_ImageView_panel { + width: 100%; + height: 68px; position: absolute; z-index: 1000; align-self: flex-start; pointer-events: all; display: flex; justify-content: space-between; - width: 100%; + align-items: center; } .mx_ImageView_toolbar { right: 0; - padding: 50px 50px 0 0; + padding-right: 18px; display: flex; + align-items: center; +} + +.mx_ImageView_toolbar_buttons { + display: flex; + align-items: center; } .mx_ImageView_label { left: 0; - padding: 50px 0 0 50px; + padding-left: 18px; text-align: left; display: flex; justify-content: center; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e3dbe2a411..fd9d84b900 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -227,7 +227,7 @@ export default class ImageView extends React.Component { if (mayRedact) { redactButton = ( - { + { ); } @@ -263,24 +263,26 @@ export default class ImageView extends React.Component { { metadata }
- { redactButton } - - { - - - { - - - { - - - { - - - { - +
+ { redactButton } + + { + + + { + + + { + + + { + + + { + +
- { + {
From 899ce1f605bcebbe46d44c35a6e70bae0155eac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:15:59 +0100 Subject: [PATCH 071/863] Partially fix overflow issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 14 ++++++++----- src/components/views/elements/ImageView.js | 24 ++++++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 8afd2670a7..b8fb9b81c2 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -27,14 +27,19 @@ limitations under the License. .mx_ImageView_content { width: 100%; + display: flex; + flex-direction: column; +} + +.mx_ImageView_image_wrapper { display: flex; justify-content: center; align-items: center; - + height: 100%; overflow: hidden; } -.mainImage { +.mx_ImageView_image { object-fit: contain; pointer-events: all; @@ -43,6 +48,8 @@ limitations under the License. min-width: 100px; min-height: 100px; + border-radius: 8px; + &:hover { cursor: grab; } @@ -51,7 +58,6 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - position: absolute; z-index: 1000; align-self: flex-start; pointer-events: all; @@ -62,7 +68,6 @@ limitations under the License. .mx_ImageView_toolbar { right: 0; - padding-right: 18px; display: flex; align-items: center; } @@ -74,7 +79,6 @@ limitations under the License. .mx_ImageView_label { left: 0; - padding-left: 18px; text-align: left; display: flex; justify-content: center; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index fd9d84b900..e35c8a3e15 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -286,17 +286,19 @@ export default class ImageView extends React.Component {
- +
+ +
); From 6aac8f1735b8d3bb0bfae0bfc91f8a514178edfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:20:28 +0100 Subject: [PATCH 072/863] Change padding a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index b8fb9b81c2..943758b80e 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -88,7 +88,7 @@ limitations under the License. } .mx_ImageView_button { - padding: 5px; + padding-left: 28px; } .mx_ImageView_metadata { From 13d766218aeb0f866f45ffe8e7e0b9e91bf93fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:59:43 +0100 Subject: [PATCH 073/863] Remove redact button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e35c8a3e15..e1babd8bf1 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -200,7 +200,6 @@ export default class ImageView extends React.Component { } render() { - let mayRedact = false; const showEventMeta = !!this.props.mxEvent; let metadata; @@ -210,7 +209,6 @@ export default class ImageView extends React.Component { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); if (room) { - mayRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId); const member = room.getMember(sender); if (member) sender = member.name; } @@ -223,15 +221,6 @@ export default class ImageView extends React.Component {
); } - let redactButton; - if (mayRedact) { - redactButton = ( - - { - - ); - } - const rotationDegrees = this.state.rotation + "deg"; const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; @@ -264,14 +253,7 @@ export default class ImageView extends React.Component {
- { redactButton } - - { - - - { - - + { From 8454a2d44098ed5c3c7f509c673b85e323cc90a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 12:52:08 +0100 Subject: [PATCH 074/863] Remove padding on lightboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/_common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 6e9d252659..9296b67375 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -315,6 +315,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { max-width: 100%; max-height: 100%; pointer-events: none; + padding: 0; } .mx_Dialog_header { From 3e408b3fcd6c3c8a201f3cb9affbd9bc651ef7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 12:59:40 +0100 Subject: [PATCH 075/863] Remove unused code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e1babd8bf1..0e11eda7d0 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -160,18 +160,6 @@ export default class ImageView extends React.Component { }); } - onZoomClick = () => { - if (this.state.zoom <= this.minZoom) { - this.setState({zoom: this.maxZoom}); - } else { - this.setState({ - zoom: this.minZoom, - translationX: 0, - translationY: 0, - }); - } - } - onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); From 7cd8f1135bafc69e2e3aab117e19a9379a6936c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 13:02:25 +0100 Subject: [PATCH 076/863] Quit on empty panel click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0e11eda7d0..288d3495d3 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -123,19 +123,25 @@ export default class ImageView extends React.Component { }); }; - onRotateCounterClockwiseClick = () => { + onRotateCounterClockwiseClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onRotateClockwiseClick = () => { + onRotateClockwiseClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onZoomInClick = () => { + onZoomInClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); if (this.state.zoom >= this.maxZoom) { this.setState({zoom: this.maxZoom}); return; @@ -146,7 +152,9 @@ export default class ImageView extends React.Component { }); }; - onZoomOutClick = () => { + onZoomOutClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); if (this.state.zoom <= this.minZoom) { this.setState({ zoom: this.minZoom, @@ -160,6 +168,10 @@ export default class ImageView extends React.Component { }); } + onPanelClick = (ev) => { + this.props.onFinished(); + } + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -235,10 +247,7 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
-
- { metadata } -
+
From fafb8d43a37b039dcc8e98a0b2d192e2db05e22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 14:16:58 +0100 Subject: [PATCH 077/863] Fix padding according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 943758b80e..17e2167494 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -64,6 +64,7 @@ limitations under the License. display: flex; justify-content: space-between; align-items: center; + padding: 0 16px 0 32px; } .mx_ImageView_toolbar { From a6bb203a4b2e165b9c5a505213ecfab6b4dc6c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 14:43:33 +0100 Subject: [PATCH 078/863] Redo icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 57 ++++++++++++++++++-- src/components/views/elements/ImageView.js | 63 ++++++++++++++++------ 2 files changed, 99 insertions(+), 21 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 17e2167494..ddc51cf583 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -64,10 +64,10 @@ limitations under the License. display: flex; justify-content: space-between; align-items: center; - padding: 0 16px 0 32px; } .mx_ImageView_toolbar { + padding-right: 16px; right: 0; display: flex; align-items: center; @@ -78,18 +78,65 @@ limitations under the License. align-items: center; } -.mx_ImageView_label { +.mx_ImageView_info_wrapper { + padding-left: 32px; left: 0; text-align: left; display: flex; justify-content: center; - flex-direction: column; - max-width: 240px; + flex-direction: row; color: $lightbox-fg-color; + align-items: center; +} + +.mx_ImageView_info { + padding-left: 12px; } .mx_ImageView_button { - padding-left: 28px; + padding-left: 24px; + display: block; + + &::before { + content: ''; + height: 22px; + width: 22px; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + display: block; + background-color: $icon-button-color; + } +} + +.mx_ImageView_button_rotateCW::before { + mask-image: url('$(res)/img/image-view/rotate-cw.svg'); +} + +.mx_ImageView_button_rotateCCW::before { + mask-image: url('$(res)/img/image-view/rotate-ccw.svg'); +} + +.mx_ImageView_button_zoomOut::before { + mask-image: url('$(res)/img/image-view/zoom-out.svg'); +} + +.mx_ImageView_button_zoomIn::before { + mask-image: url('$(res)/img/image-view/zoom-in.svg'); +} + +.mx_ImageView_button_download::before { + mask-image: url('$(res)/img/image-view/download.svg'); +} + +.mx_ImageView_button_close { + padding-left: 32px; + &::before { + width: 32px; + height: 32px; + mask-image: url('$(res)/img/image-view/close.svg'); + background-color: none; + } } .mx_ImageView_metadata { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 288d3495d3..28a087f77d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -20,11 +20,12 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; -import AccessibleButton from "./AccessibleButton"; +import AccessibleTooltipButton from "./AccessibleTooltipButton"; import Modal from "../../../Modal"; import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; +import MemberAvatar from "../avatars/MemberAvatar"; export default class ImageView extends React.Component { static propTypes = { @@ -214,10 +215,7 @@ export default class ImageView extends React.Component { } metadata = (
- { _t('Uploaded on %(date)s by %(user)s', { - date: formatDate(new Date(this.props.mxEvent.getTs())), - user: sender, - }) } + { formatDate(new Date(this.props.mxEvent.getTs())) }
); } @@ -236,6 +234,8 @@ export default class ImageView extends React.Component { rotate(${rotationDegrees})`, }; + const event = this.props.mxEvent; + return (
+
+ +
+ { event.sender ? event.sender.name : event.getSender() } + { metadata } +
+
- - { - - - { - - - { + + + + + + + + + + +
- - { -
From b068a4c05504c4b5104a97c99f23f899e7dfb301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 16:28:12 +0100 Subject: [PATCH 079/863] Make download into a button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 28a087f77d..8251af1a5d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -169,6 +169,15 @@ export default class ImageView extends React.Component { }); } + onDownloadClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + const a = document.createElement("a"); + a.href = this.props.src; + a.download = this.props.name; + a.click(); + } + onPanelClick = (ev) => { this.props.onFinished(); } @@ -281,13 +290,11 @@ export default class ImageView extends React.Component { title={_t("Zoom in")} onClick={ this.onZoomInClick }> - - + onClick={ this.onDownloadClick }> + Date: Wed, 24 Feb 2021 18:10:50 +0100 Subject: [PATCH 080/863] Add more icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/image-view/more.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 res/img/image-view/more.svg diff --git a/res/img/image-view/more.svg b/res/img/image-view/more.svg new file mode 100644 index 0000000000..4f5fa6f9b9 --- /dev/null +++ b/res/img/image-view/more.svg @@ -0,0 +1,3 @@ + + + From 83e1a7a70781c026c62c03dd8f2652125e44da9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 18:13:12 +0100 Subject: [PATCH 081/863] Add more button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ++++ src/components/views/elements/ImageView.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ddc51cf583..bb99454152 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -129,6 +129,10 @@ limitations under the License. mask-image: url('$(res)/img/image-view/download.svg'); } +.mx_ImageView_button_more::before { + mask-image: url('$(res)/img/image-view/more.svg'); +} + .mx_ImageView_button_close { padding-left: 32px; &::before { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8251af1a5d..cd104cce37 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -295,6 +295,10 @@ export default class ImageView extends React.Component { title={_t("Download")} onClick={ this.onDownloadClick }> + Date: Wed, 24 Feb 2021 18:24:44 +0100 Subject: [PATCH 082/863] Don't show info if no event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index cd104cce37..c00b62ecc8 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -245,6 +245,30 @@ export default class ImageView extends React.Component { const event = this.props.mxEvent; + let info; + if (event) { + info = ( +
+ +
+ { event.sender ? event.sender.name : event.getSender() } + { metadata } +
+
+ ); + } else { + // If there is no event - we're viewing an avatar, we set + // an empty div here, since the panel uses space-between + // and we want the same placement of elements + info = ( +
+ ); + } + return (
-
- -
- { event.sender ? event.sender.name : event.getSender() } - { metadata } -
-
+ {info}
Date: Wed, 24 Feb 2021 19:11:08 +0100 Subject: [PATCH 083/863] Pass permallinkCreator to ImageView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 1 + src/components/views/messages/MImageBody.js | 3 +++ src/components/views/messages/MessageEvent.js | 3 +++ src/components/views/rooms/EventTile.js | 1 + 4 files changed, 8 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index c00b62ecc8..ede4cc9623 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -42,6 +42,7 @@ export default class ImageView extends React.Component { // properties above, which let us use lightboxes to display images which aren't associated // with events. mxEvent: PropTypes.object, + permalinkCreator: PropTypes.object, }; constructor(props) { diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 8456a5bd09..616f2b1cc8 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -38,6 +38,8 @@ export default class MImageBody extends React.Component { /* the maximum image height to use */ maxImageHeight: PropTypes.number, + + permalinkCreator: PropTypes.object, }; static contextType = MatrixClientContext; @@ -103,6 +105,7 @@ export default class MImageBody extends React.Component { src: httpUrl, name: content.body && content.body.length > 0 ? content.body : _t('Attachment'), mxEvent: this.props.mxEvent, + permalinkCreator: this.props.permalinkCreator, }; if (content.info) { diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index f93813fe79..daee6558c9 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -44,6 +44,8 @@ export default class MessageEvent extends React.Component { /* the maximum image height to use, if the event is an image */ maxImageHeight: PropTypes.number, + + permalinkCreator: PropTypes.object, }; constructor(props) { @@ -120,6 +122,7 @@ export default class MessageEvent extends React.Component { editState={this.props.editState} onHeightChanged={this.props.onHeightChanged} onMessageAllowed={this.onTileUpdate} + permalinkCreator={this.props.permalinkCreator} />; } } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index c856919f5a..fb0fa7d6d2 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -968,6 +968,7 @@ export default class EventTile extends React.Component { highlights={this.props.highlights} highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} + permalinkCreator={this.props.permalinkCreator} onHeightChanged={this.props.onHeightChanged} /> { keyRequestInfo } { reactionsRow } From 9312becee56ee0dd5b704e0913b9fc8fc2c1119a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:17:33 +0100 Subject: [PATCH 084/863] Add context menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 48 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index ede4cc9623..5f77b1ccfa 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { createRef } from 'react'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; @@ -26,6 +26,9 @@ import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; import MemberAvatar from "../avatars/MemberAvatar"; +import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton"; +import MessageContextMenu from "../context_menus/MessageContextMenu"; +import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; export default class ImageView extends React.Component { static propTypes = { @@ -53,9 +56,11 @@ export default class ImageView extends React.Component { translationX: 0, translationY: 0, moving: false, + contextMenuDisplay: false, }; } + contextMenuButton = createRef(); initX = 0; initY = 0; lastX = 0; @@ -179,6 +184,20 @@ export default class ImageView extends React.Component { a.click(); } + onOpenContextMenu = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + this.setState({ + contextMenuDisplay: true, + }); + } + + onCloseContextMenu = () => { + this.setState({ + contextMenuDisplay: false, + }); + } + onPanelClick = (ev) => { this.props.onFinished(); } @@ -210,6 +229,30 @@ export default class ImageView extends React.Component { this.setState({moving: false}); } + renderContextMenu() { + let contextMenu = null; + if (this.state.contextMenuDisplay) { + contextMenu = ( + + + + ); + } + + return ( + + { contextMenu } + + ); + } + render() { const showEventMeta = !!this.props.mxEvent; @@ -313,12 +356,15 @@ export default class ImageView extends React.Component { + {this.renderContextMenu()}
From 81698a2714ff2637d522831c109f4e7a0750fbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:31:19 +0100 Subject: [PATCH 085/863] Fix pointer-events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index bb99454152..cd5f9a247b 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -58,9 +58,7 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - z-index: 1000; align-self: flex-start; - pointer-events: all; display: flex; justify-content: space-between; align-items: center; @@ -76,6 +74,7 @@ limitations under the License. .mx_ImageView_toolbar_buttons { display: flex; align-items: center; + pointer-events: all; } .mx_ImageView_info_wrapper { @@ -91,6 +90,7 @@ limitations under the License. .mx_ImageView_info { padding-left: 12px; + pointer-events: all; } .mx_ImageView_button { From 2021e4e345c7cfc554421509e88253dcddedcf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:33:17 +0100 Subject: [PATCH 086/863] Remove ugly workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 5f77b1ccfa..dc785aabb2 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -131,24 +131,18 @@ export default class ImageView extends React.Component { }; onRotateCounterClockwiseClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; onRotateClockwiseClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; onZoomInClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); if (this.state.zoom >= this.maxZoom) { this.setState({zoom: this.maxZoom}); return; @@ -160,8 +154,6 @@ export default class ImageView extends React.Component { }; onZoomOutClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); if (this.state.zoom <= this.minZoom) { this.setState({ zoom: this.minZoom, @@ -176,8 +168,6 @@ export default class ImageView extends React.Component { } onDownloadClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const a = document.createElement("a"); a.href = this.props.src; a.download = this.props.name; @@ -185,8 +175,6 @@ export default class ImageView extends React.Component { } onOpenContextMenu = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); this.setState({ contextMenuDisplay: true, }); @@ -198,10 +186,6 @@ export default class ImageView extends React.Component { }); } - onPanelClick = (ev) => { - this.props.onFinished(); - } - onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -324,7 +308,7 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
+
{info}
From 7293181552b3bad2b24ec9f9dae80597be82ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:33:22 +0100 Subject: [PATCH 087/863] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 74617e17af..e94e1bbae6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1861,14 +1861,10 @@ "collapse": "collapse", "expand": "expand", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", - "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", - "Zoom": "Zoom", + "Rotate Right": "Rotate Right", + "Rotate Left": "Rotate Left", "Zoom out": "Zoom out", "Zoom in": "Zoom in", - "Rotate Left": "Rotate Left", - "Rotate counter-clockwise": "Rotate counter-clockwise", - "Rotate Right": "Rotate Right", - "Rotate clockwise": "Rotate clockwise", "Download": "Download", "Information": "Information", "Language Dropdown": "Language Dropdown", From 983895289c30a9626816ba0ef971cbc01d4eefbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 20:04:25 +0100 Subject: [PATCH 088/863] Update info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 7 +--- src/components/views/elements/ImageView.js | 47 +++++++++------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index cd5f9a247b..11ef9ec692 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -91,6 +91,8 @@ limitations under the License. .mx_ImageView_info { padding-left: 12px; pointer-events: all; + display: flex; + flex-direction: column; } .mx_ImageView_button { @@ -142,8 +144,3 @@ limitations under the License. background-color: none; } } - -.mx_ImageView_metadata { - font-size: $font-15px; - opacity: 0.5; -} diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index dc785aabb2..1a93b5c3f7 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -18,7 +18,6 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; import AccessibleTooltipButton from "./AccessibleTooltipButton"; import Modal from "../../../Modal"; @@ -29,6 +28,8 @@ import MemberAvatar from "../avatars/MemberAvatar"; import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton"; import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; +import MessageTimestamp from "../messages/MessageTimestamp"; +import SenderProfile from '../messages/SenderProfile'; export default class ImageView extends React.Component { static propTypes = { @@ -240,22 +241,6 @@ export default class ImageView extends React.Component { render() { const showEventMeta = !!this.props.mxEvent; - let metadata; - if (showEventMeta) { - // Figure out the sender, defaulting to mxid - let sender = this.props.mxEvent.getSender(); - const cli = MatrixClientPeg.get(); - const room = cli.getRoom(this.props.mxEvent.getRoomId()); - if (room) { - const member = room.getMember(sender); - if (member) sender = member.name; - } - - metadata = (
- { formatDate(new Date(this.props.mxEvent.getTs())) } -
); - } - const rotationDegrees = this.state.rotation + "deg"; const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; @@ -271,20 +256,28 @@ export default class ImageView extends React.Component { rotate(${rotationDegrees})`, }; - const event = this.props.mxEvent; - let info; - if (event) { + if (showEventMeta) { + const mxEvent = this.props.mxEvent; + + const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); + const messageTimestamp = ( + + ); + const avatar = ( + + ); + info = (
- + {avatar}
- { event.sender ? event.sender.name : event.getSender() } - { metadata } + {senderName} + {messageTimestamp}
); From 6008a6f9fa09060f2c6851e61a5d416d7a28469d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 20:07:41 +0100 Subject: [PATCH 089/863] Use showTwelveHourTimestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 1a93b5c3f7..cbe5b0592f 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -29,7 +29,7 @@ import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/Cont import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; -import SenderProfile from '../messages/SenderProfile'; +import SettingsStore from "../../../settings/SettingsStore"; export default class ImageView extends React.Component { static propTypes = { @@ -259,10 +259,11 @@ export default class ImageView extends React.Component { let info; if (showEventMeta) { const mxEvent = this.props.mxEvent; + const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const messageTimestamp = ( - + ); const avatar = ( Date: Wed, 24 Feb 2021 20:14:12 +0100 Subject: [PATCH 090/863] Make permalink clickable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index cbe5b0592f..073b9dddbd 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -30,6 +30,8 @@ import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; import SettingsStore from "../../../settings/SettingsStore"; +import {formatTime} from "../../../DateUtils"; +import dis from '../../../dispatcher/dispatcher'; export default class ImageView extends React.Component { static propTypes = { @@ -187,6 +189,18 @@ export default class ImageView extends React.Component { }); } + onPermalinkClicked = 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 Element when clicked. + e.preventDefault(); + dis.dispatch({ + action: 'view_room', + event_id: this.props.mxEvent.getId(), + highlighted: true, + room_id: this.props.mxEvent.getRoomId(), + }); + }; + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -260,10 +274,21 @@ export default class ImageView extends React.Component { if (showEventMeta) { const mxEvent = this.props.mxEvent; const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); + let permalink = "#"; + if (this.props.permalinkCreator) { + permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); + } const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const messageTimestamp = ( - + + + + ); const avatar = ( Date: Wed, 24 Feb 2021 20:41:20 +0100 Subject: [PATCH 091/863] Remove rounded border --- res/css/views/elements/_ImageView.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 11ef9ec692..f904d31330 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -48,8 +48,6 @@ limitations under the License. min-width: 100px; min-height: 100px; - border-radius: 8px; - &:hover { cursor: grab; } From d58c17ff3bfb75c61329979b7ea8d3ccd69bfe70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 07:50:53 +0100 Subject: [PATCH 092/863] Show grabbing cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ---- src/components/views/elements/ImageView.js | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index f904d31330..d864ad9adf 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -47,10 +47,6 @@ limitations under the License. max-height: 70vh; min-width: 100px; min-height: 100px; - - &:hover { - cursor: grab; - } } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 073b9dddbd..51e700c481 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -264,6 +264,7 @@ export default class ImageView extends React.Component { * we would apply the translation to an already rotated * image causing it translate in the wrong direction. */ const style = { + cursor: this.state.moving ? "grabbing" : "grab", transform: `translateX(${translatePixelsX}) translateY(${translatePixelsY}) scale(${zoomPercentage}) From fe8e90f92063d8efd6f1b880e6f70e994cb84f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 07:51:38 +0100 Subject: [PATCH 093/863] Change comment styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 51e700c481..d91b7c8aba 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -72,8 +72,8 @@ export default class ImageView extends React.Component { maxZoom = 300; componentDidMount() { - /* We have to use addEventListener() because the listener - * needs to be passive in order to work with Chromium */ + // We have to use addEventListener() because the listener + // needs to be passive in order to work with Chromium this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } @@ -259,10 +259,10 @@ export default class ImageView extends React.Component { const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; const translatePixelsY = this.state.translationY + "px"; - /* The order of the values is important! - * First, we translate and only then we rotate, otherwise - * we would apply the translation to an already rotated - * image causing it translate in the wrong direction. */ + // The order of the values is important! + // First, we translate and only then we rotate, otherwise + // we would apply the translation to an already rotated + // image causing it translate in the wrong direction. const style = { cursor: this.state.moving ? "grabbing" : "grab", transform: `translateX(${translatePixelsX}) From 5a9e1a14822e5f6ecb5e0fed186e9f021921286c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:10:54 +0100 Subject: [PATCH 094/863] Fix close icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 7 ++++--- res/img/image-view/close.svg | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index d864ad9adf..9b77f70262 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -90,7 +90,7 @@ limitations under the License. } .mx_ImageView_button { - padding-left: 24px; + margin-left: 24px; display: block; &::before { @@ -130,11 +130,12 @@ limitations under the License. } .mx_ImageView_button_close { - padding-left: 32px; + border-radius: 100%; + background: #21262C; &::before { width: 32px; height: 32px; mask-image: url('$(res)/img/image-view/close.svg'); - background-color: none; + mask-size: 40%; } } diff --git a/res/img/image-view/close.svg b/res/img/image-view/close.svg index f849425264..d603b7f5cc 100644 --- a/res/img/image-view/close.svg +++ b/res/img/image-view/close.svg @@ -1,4 +1,3 @@ - - - + + From 83de84972ed143750f0f633f962398818643207b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:13:27 +0100 Subject: [PATCH 095/863] Close onPermalinkClicked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d91b7c8aba..f7826d45fe 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -199,6 +199,7 @@ export default class ImageView extends React.Component { highlighted: true, room_id: this.props.mxEvent.getRoomId(), }); + this.props.onFinished(); }; onStartMoving = ev => { From b18622efe492860153bf60a92ea56ce49dbd4480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:20:34 +0100 Subject: [PATCH 096/863] Show full date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 6 +++--- src/components/views/messages/MessageTimestamp.js | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index f7826d45fe..5acd36bde0 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -30,7 +30,7 @@ import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; import SettingsStore from "../../../settings/SettingsStore"; -import {formatTime} from "../../../DateUtils"; +import {formatFullDate} from "../../../DateUtils"; import dis from '../../../dispatcher/dispatcher'; export default class ImageView extends React.Component { @@ -286,9 +286,9 @@ export default class ImageView extends React.Component { - + ); diff --git a/src/components/views/messages/MessageTimestamp.js b/src/components/views/messages/MessageTimestamp.js index 199a6f47ce..26b8096c4f 100644 --- a/src/components/views/messages/MessageTimestamp.js +++ b/src/components/views/messages/MessageTimestamp.js @@ -23,13 +23,18 @@ export default class MessageTimestamp extends React.Component { static propTypes = { ts: PropTypes.number.isRequired, showTwelveHour: PropTypes.bool, + showFullDate: PropTypes.bool, }; render() { const date = new Date(this.props.ts); return ( - { formatTime(date, this.props.showTwelveHour) } + { + this.props.showFullDate ? + formatFullDate(date, this.props.showTwelveHour) : + formatTime(date, this.props.showTwelveHour) + } ); } From 273753a42a9a122b1f6044c2840ef4830896781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:25:34 +0100 Subject: [PATCH 097/863] Fix hex formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 9b77f70262..45968ddaa5 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -131,7 +131,7 @@ limitations under the License. .mx_ImageView_button_close { border-radius: 100%; - background: #21262C; + background: #21262c; &::before { width: 32px; height: 32px; From a4130cb7f35ae8325b2b68b457246e23db02158a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:23:27 +0100 Subject: [PATCH 098/863] Revert trash icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/feather-customised/trash.custom.svg | 89 ++------------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index 589bb0a4e5..70eeaf35cd 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -1,82 +1,7 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - + + + + + + + \ No newline at end of file From 7fc375805628a6b7a11777b70f8c06011e132b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:23:59 +0100 Subject: [PATCH 099/863] Add newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/feather-customised/trash.custom.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index 70eeaf35cd..dc1985ddb2 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -4,4 +4,4 @@ - \ No newline at end of file + From 984b4372db4e6c56638dc0f6d419acb5b4e8a324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:25:24 +0100 Subject: [PATCH 100/863] Remove trash red icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/trash-red.svg | 89 ------------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 res/img/trash-red.svg diff --git a/res/img/trash-red.svg b/res/img/trash-red.svg deleted file mode 100644 index 0b1d201d2e..0000000000 --- a/res/img/trash-red.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - From 4f3fe3d236640f744eec9f966b0a0260d698f469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:09:52 +0100 Subject: [PATCH 101/863] Add comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/MImageBody.js | 1 + src/components/views/messages/MessageEvent.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 616f2b1cc8..b1d5995121 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -39,6 +39,7 @@ export default class MImageBody extends React.Component { /* the maximum image height to use */ maxImageHeight: PropTypes.number, + /* the permalinkCreator */ permalinkCreator: PropTypes.object, }; diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index daee6558c9..fe0bb19fda 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -45,6 +45,7 @@ export default class MessageEvent extends React.Component { /* the maximum image height to use, if the event is an image */ maxImageHeight: PropTypes.number, + /* the permalinkCreator */ permalinkCreator: PropTypes.object, }; From d0dea91e92986cbcb07d53888a4014cb69a0ba6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:16:40 +0100 Subject: [PATCH 102/863] contextMenuDisplay -> contextMenuDisplayed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 5acd36bde0..bbadad9778 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -59,7 +59,7 @@ export default class ImageView extends React.Component { translationX: 0, translationY: 0, moving: false, - contextMenuDisplay: false, + contextMenuDisplayed: false, }; } @@ -179,13 +179,13 @@ export default class ImageView extends React.Component { onOpenContextMenu = (ev) => { this.setState({ - contextMenuDisplay: true, + contextMenuDisplayed: true, }); } onCloseContextMenu = () => { this.setState({ - contextMenuDisplay: false, + contextMenuDisplayed: false, }); } @@ -231,7 +231,7 @@ export default class ImageView extends React.Component { renderContextMenu() { let contextMenu = null; - if (this.state.contextMenuDisplay) { + if (this.state.contextMenuDisplayed) { contextMenu = ( Date: Thu, 25 Feb 2021 11:19:50 +0100 Subject: [PATCH 103/863] ZOOM shouldn't be a part of the class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index bbadad9778..6e9066ccbf 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -33,6 +33,9 @@ import SettingsStore from "../../../settings/SettingsStore"; import {formatFullDate} from "../../../DateUtils"; import dis from '../../../dispatcher/dispatcher'; +const MIN_ZOOM = 100; +const MAX_ZOOM = 300; + export default class ImageView extends React.Component { static propTypes = { src: PropTypes.string.isRequired, // the source of the image being displayed @@ -68,8 +71,6 @@ export default class ImageView extends React.Component { initY = 0; lastX = 0; lastY = 0; - minZoom = 100; - maxZoom = 300; componentDidMount() { // We have to use addEventListener() because the listener @@ -94,16 +95,16 @@ export default class ImageView extends React.Component { ev.preventDefault(); const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= this.minZoom) { + if (newZoom <= MIN_ZOOM) { this.setState({ - zoom: this.minZoom, + zoom: MIN_ZOOM, translationX: 0, translationY: 0, }); return; } - if (newZoom >= this.maxZoom) { - this.setState({zoom: this.maxZoom}); + if (newZoom >= MAX_ZOOM) { + this.setState({zoom: MAX_ZOOM}); return; } @@ -146,8 +147,8 @@ export default class ImageView extends React.Component { }; onZoomInClick = (ev) => { - if (this.state.zoom >= this.maxZoom) { - this.setState({zoom: this.maxZoom}); + if (this.state.zoom >= MAX_ZOOM) { + this.setState({zoom: MAX_ZOOM}); return; } @@ -157,9 +158,9 @@ export default class ImageView extends React.Component { }; onZoomOutClick = (ev) => { - if (this.state.zoom <= this.minZoom) { + if (this.state.zoom <= MIN_ZOOM) { this.setState({ - zoom: this.minZoom, + zoom: MIN_ZOOM, translationX: 0, translationY: 0, }); From 80ce4da9b66a2d2cf0044bb4ef59f47410a72a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:21:04 +0100 Subject: [PATCH 104/863] Remove onRedactClick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 6e9066ccbf..dd5ad56a4a 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -113,27 +113,6 @@ export default class ImageView extends React.Component { }); } - onRedactClick = () => { - const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); - Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, { - onFinished: (proceed) => { - if (!proceed) return; - this.props.onFinished(); - MatrixClientPeg.get().redactEvent( - this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(), - ).catch(function(e) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - // display error message stating you couldn't delete this. - const code = e.errcode || e.statusCode; - Modal.createTrackedDialog('You cannot delete this image.', '', ErrorDialog, { - title: _t('Error'), - description: _t('You cannot delete this image. (%(code)s)', {code: code}), - }); - }); - }, - }); - }; - onRotateCounterClockwiseClick = (ev) => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; From 4c377ae037fc8bbc1a8a320092550470de8a0b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:23:14 +0100 Subject: [PATCH 105/863] Consistent evs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index dd5ad56a4a..c14b8c60bb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -113,19 +113,19 @@ export default class ImageView extends React.Component { }); } - onRotateCounterClockwiseClick = (ev) => { + onRotateCounterClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onRotateClockwiseClick = (ev) => { + onRotateClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onZoomInClick = (ev) => { + onZoomInClick = () => { if (this.state.zoom >= MAX_ZOOM) { this.setState({zoom: MAX_ZOOM}); return; @@ -136,7 +136,7 @@ export default class ImageView extends React.Component { }); }; - onZoomOutClick = (ev) => { + onZoomOutClick = () => { if (this.state.zoom <= MIN_ZOOM) { this.setState({ zoom: MIN_ZOOM, @@ -150,14 +150,14 @@ export default class ImageView extends React.Component { }); } - onDownloadClick = (ev) => { + onDownloadClick = () => { const a = document.createElement("a"); a.href = this.props.src; a.download = this.props.name; a.click(); } - onOpenContextMenu = (ev) => { + onOpenContextMenu = () => { this.setState({ contextMenuDisplayed: true, }); @@ -169,10 +169,10 @@ export default class ImageView extends React.Component { }); } - onPermalinkClicked = e => { + onPermalinkClicked = (ev) => { // 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 Element when clicked. - e.preventDefault(); + ev.preventDefault(); dis.dispatch({ action: 'view_room', event_id: this.props.mxEvent.getId(), @@ -182,7 +182,7 @@ export default class ImageView extends React.Component { this.props.onFinished(); }; - onStartMoving = ev => { + onStartMoving = (ev) => { ev.stopPropagation(); ev.preventDefault(); @@ -191,7 +191,7 @@ export default class ImageView extends React.Component { this.initY = ev.pageY - this.lastY; } - onMoving = ev => { + onMoving = (ev) => { ev.stopPropagation(); ev.preventDefault(); @@ -205,7 +205,7 @@ export default class ImageView extends React.Component { }); } - onEndMoving = ev => { + onEndMoving = () => { this.setState({moving: false}); } From 436a17bcc91feca49de9c454646a7efeda1404e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:26:34 +0100 Subject: [PATCH 106/863] Remove imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index c14b8c60bb..ea424d9925 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -17,11 +17,8 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; -import {MatrixClientPeg} from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; import AccessibleTooltipButton from "./AccessibleTooltipButton"; -import Modal from "../../../Modal"; -import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; import MemberAvatar from "../avatars/MemberAvatar"; From dc283241aa08d9cdc24bc35552c8703b38348b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:28:42 +0100 Subject: [PATCH 107/863] Remove wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 ------ src/components/views/elements/ImageView.js | 2 -- 2 files changed, 8 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 45968ddaa5..4b786a244b 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -22,12 +22,6 @@ limitations under the License. display: flex; width: 100%; height: 100%; -} - -.mx_ImageView_content { - width: 100%; - - display: flex; flex-direction: column; } diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index ea424d9925..21b16ed89d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -305,7 +305,6 @@ export default class ImageView extends React.Component { className="mx_ImageView" ref={ref => this.focusLock = ref} > -
{info}
@@ -363,7 +362,6 @@ export default class ImageView extends React.Component { onMouseLeave={this.onEndMoving} />
-
); } From 1955fff08cfb11761d9328718852270367269719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:50:50 +0100 Subject: [PATCH 108/863] CSS cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 31 +++------ src/components/views/elements/ImageView.js | 76 +++++++++++----------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 4b786a244b..626878b03e 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -34,10 +34,9 @@ limitations under the License. } .mx_ImageView_image { - object-fit: contain; pointer-events: all; - max-width: 100vw; + max-width: 70vw; max-height: 70vh; min-width: 100px; min-height: 100px; @@ -46,43 +45,33 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - align-self: flex-start; display: flex; justify-content: space-between; align-items: center; } -.mx_ImageView_toolbar { - padding-right: 16px; - right: 0; - display: flex; - align-items: center; -} - -.mx_ImageView_toolbar_buttons { - display: flex; - align-items: center; - pointer-events: all; -} - .mx_ImageView_info_wrapper { + pointer-events: all; padding-left: 32px; - left: 0; - text-align: left; display: flex; - justify-content: center; flex-direction: row; - color: $lightbox-fg-color; align-items: center; + color: $lightbox-fg-color; } .mx_ImageView_info { padding-left: 12px; - pointer-events: all; display: flex; flex-direction: column; } +.mx_ImageView_toolbar { + padding-right: 16px; + pointer-events: all; + display: flex; + align-items: center; +} + .mx_ImageView_button { margin-left: 24px; display: block; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 21b16ed89d..15bb25d473 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -308,45 +308,43 @@ export default class ImageView extends React.Component {
{info}
-
- - - - - - - - - - - - - - {this.renderContextMenu()} -
+ + + + + + + + + + + + + + {this.renderContextMenu()}
From fc32ceade74c072585c58905e153027f9ab766f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:57:39 +0100 Subject: [PATCH 109/863] i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e94e1bbae6..982ee44452 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1860,7 +1860,6 @@ "Please create a new issue on GitHub so that we can investigate this bug.": "Please create a new issue on GitHub so that we can investigate this bug.", "collapse": "collapse", "expand": "expand", - "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Rotate Right": "Rotate Right", "Rotate Left": "Rotate Left", "Zoom out": "Zoom out", From ad85764a8e3d1025c76db29e9f53ba690ac2eb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:23:32 +0100 Subject: [PATCH 110/863] Fix timeline expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 + res/css/structures/_RoomView.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index f05f24d0d7..9597083e9c 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -19,6 +19,7 @@ limitations under the License. flex-direction: row; min-width: 0; height: 100%; + justify-content: space-between; } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index cd8c640132..5240a0650f 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -121,6 +121,7 @@ limitations under the License. position: relative; //for .mx_RoomView_auxPanel_fullHeight display: flex; flex-direction: column; + width: 100%; } .mx_RoomView_body { From aa4ec9fca1a65202306ee77d705b15f1782de188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:27:52 +0100 Subject: [PATCH 111/863] Make $droptarget-bg-color more opaque MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/light/css/_light.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 1c89d83c01..ea7b0472e0 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -68,7 +68,7 @@ $groupFilterPanel-bg-color: rgba(232, 232, 232, 0.77); $plinth-bg-color: $secondary-accent-color; // used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.5); +$droptarget-bg-color: rgba(255,255,255,0.95); // used by AddressSelector $selected-color: $secondary-accent-color; From 8551855a5626d06e6feda6f849f50b26af85a194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:30:14 +0100 Subject: [PATCH 112/863] Add $droptarget-bg-color to the dark theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/dark/css/_dark.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index a878aa3cdd..f6f415ce70 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -42,6 +42,9 @@ $preview-bar-bg-color: $header-panel-bg-color; $groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82); $inverted-bg-color: $base-color; +// used by RoomDropTarget +$droptarget-bg-color: rgba(21,25,30,0.95); + // used by AddressSelector $selected-color: $room-highlight-color; From a3001f77e46e7c3ac3479a70eb56db312c6f1361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:30:39 +0100 Subject: [PATCH 113/863] Remove rounded corners of the drop area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5240a0650f..e80ac062b6 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -30,9 +30,6 @@ limitations under the License. pointer-events: none; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - background-color: $droptarget-bg-color; position: absolute; From 26b70b62280b7f4fa21c6287faf20a309a399abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:32:04 +0100 Subject: [PATCH 114/863] Remove label background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index e80ac062b6..8ba31fac20 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -42,11 +42,6 @@ limitations under the License. .mx_RoomView_fileDropTargetLabel { position: absolute; - - border-radius: 10px; - padding: 10px; - - background-color: $menu-bg-color; } .mx_RoomView_auxPanel { From 6a7340e8be3844881b1c114d74687983b9c0ba20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:46:48 +0100 Subject: [PATCH 115/863] Use new upload icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/upload-big.svg | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/res/img/upload-big.svg b/res/img/upload-big.svg index 6099c2e976..9a6a265fdb 100644 --- a/res/img/upload-big.svg +++ b/res/img/upload-big.svg @@ -1,19 +1,3 @@ - - - - icons_upload_drop - Created with bin/sketchtool. - - - - - - - - - - - - - + + From 1c48804d96c3cdda150e38e2d520a2268dd5728e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 19:28:08 +0100 Subject: [PATCH 116/863] Remove unnecessary class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 4 ---- src/components/views/rooms/AuxPanel.tsx | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 8ba31fac20..28d8d1e196 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -40,10 +40,6 @@ limitations under the License. align-items: center; } -.mx_RoomView_fileDropTargetLabel { - position: absolute; -} - .mx_RoomView_auxPanel { min-width: 0px; width: 100%; diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 7966643084..543a50d59f 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -156,7 +156,7 @@ export default class AuxPanel extends React.Component { if (this.props.draggingFile) { fileDropTarget = (
-
+

{ _t("Drop file here to upload") } From 43e1144ae7ca7eff08c1666b4a179ba828d41432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 19:36:55 +0100 Subject: [PATCH 117/863] Don't use TintableSVG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This seemed to have caused a little lag and it was unnecessary Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 543a50d59f..c9150d588f 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -150,14 +150,12 @@ export default class AuxPanel extends React.Component { } render() { - const TintableSvg = sdk.getComponent("elements.TintableSvg"); - let fileDropTarget = null; if (this.props.draggingFile) { fileDropTarget = (
- +
{ _t("Drop file here to upload") }
From 7277c285a9326928555e05766ee3ce33603de18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 20:10:38 +0100 Subject: [PATCH 118/863] Fix weird crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index c9150d588f..cc3408476c 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -155,7 +155,7 @@ export default class AuxPanel extends React.Component { fileDropTarget = (
- +
{ _t("Drop file here to upload") }
From 49ea9a4788243346b20fcf5b4b79f46a7c3a80ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 20:10:58 +0100 Subject: [PATCH 119/863] Remove sdk import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index cc3408476c..59ea8e237a 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import { Room } from 'matrix-js-sdk/src/models/room' -import * as sdk from '../../../index'; import dis from "../../../dispatcher/dispatcher"; import * as ObjectUtils from '../../../ObjectUtils'; import AppsDrawer from './AppsDrawer'; From 563620484df6fa79f666ef72f414838cf0e342f1 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 25 Feb 2021 14:39:20 -0500 Subject: [PATCH 120/863] Support replying with a message command Signed-off-by: Robin Townsend --- src/SlashCommands.tsx | 28 +++++++----- .../views/rooms/SendMessageComposer.js | 44 +++++++++++++------ 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 6b5f261374..06468c135e 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -20,6 +20,7 @@ limitations under the License. import * as React from 'react'; +import { ContentHelpers } from 'matrix-js-sdk'; import {MatrixClientPeg} from './MatrixClientPeg'; import dis from './dispatcher/dispatcher'; import * as sdk from './index'; @@ -126,10 +127,10 @@ export class Command { return this.getCommand() + " " + this.args; } - run(roomId: string, args: string, cmd: string) { + run(roomId: string, args: string) { // if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me` if (!this.runFn) return reject(_t("Command error")); - return this.runFn.bind(this)(roomId, args, cmd); + return this.runFn.bind(this)(roomId, args); } getUsage() { @@ -163,7 +164,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -176,7 +177,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -189,7 +190,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -202,7 +203,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -211,7 +212,7 @@ export const Commands = [ args: '', description: _td('Sends a message as plain text, without interpreting it as markdown'), runFn: function(roomId, messages) { - return success(MatrixClientPeg.get().sendTextMessage(roomId, messages)); + return success(ContentHelpers.makeTextMessage(messages)); }, category: CommandCategories.messages, }), @@ -220,7 +221,7 @@ export const Commands = [ args: '', description: _td('Sends a message as html, without interpreting it as markdown'), runFn: function(roomId, messages) { - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, messages, messages)); + return success(ContentHelpers.makeHtmlMessage(messages, messages)); }, category: CommandCategories.messages, }), @@ -966,7 +967,7 @@ export const Commands = [ args: '', runFn: function(roomId, args) { if (!args) return reject(this.getUserId()); - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); + return success(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, }), @@ -976,7 +977,7 @@ export const Commands = [ args: '', runFn: function(roomId, args) { if (!args) return reject(this.getUserId()); - return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); + return success(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, }), @@ -1201,10 +1202,13 @@ export function parseCommandString(input: string) { * processing the command, or 'promise' if a request was sent out. * Returns null if the input didn't match a command. */ -export function getCommand(roomId: string, input: string) { +export function getCommand(input: string) { const {cmd, args} = parseCommandString(input); if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) { - return () => CommandMap.get(cmd).run(roomId, args, cmd); + return { + cmd: CommandMap.get(cmd), + args, + }; } } diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 62c474e417..d1482c0df6 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -33,7 +33,7 @@ import ReplyThread from "../elements/ReplyThread"; import {parseEvent} from '../../../editor/deserialize'; import {findEditableEvent} from '../../../utils/EventUtils'; import SendHistoryManager from "../../../SendHistoryManager"; -import {getCommand} from '../../../SlashCommands'; +import {CommandCategories, getCommand} from '../../../SlashCommands'; import * as sdk from '../../../index'; import Modal from '../../../Modal'; import {_t, _td} from '../../../languageHandler'; @@ -287,15 +287,22 @@ export default class SendMessageComposer extends React.Component { } return text + part.text; }, ""); - return [getCommand(this.props.room.roomId, commandText), commandText]; + const {cmd, args} = getCommand(commandText); + return [cmd, args, commandText]; } - async _runSlashCommand(fn) { - const cmd = fn(); - let error = cmd.error; - if (cmd.promise) { + async _runSlashCommand(cmd, args) { + const result = cmd.run(this.props.room.roomId, args); + let messageContent; + let error = result.error; + if (result.promise) { try { - await cmd.promise; + if (cmd.category === CommandCategories.messages) { + // The command returns a modified message that we need to pass on + messageContent = await result.promise; + } else { + await result.promise; + } } catch (err) { error = err; } @@ -304,7 +311,7 @@ export default class SendMessageComposer extends React.Component { console.error("Command failure: %s", error); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // assume the error is a server error when the command is async - const isServerError = !!cmd.promise; + const isServerError = !!result.promise; const title = isServerError ? _td("Server error") : _td("Command error"); let errText; @@ -322,6 +329,7 @@ export default class SendMessageComposer extends React.Component { }); } else { console.log("Command success."); + if (messageContent) return messageContent; } } @@ -330,13 +338,22 @@ export default class SendMessageComposer extends React.Component { return; } + const replyToEvent = this.props.replyToEvent; let shouldSend = true; + let content; if (!containsEmote(this.model) && this._isSlashCommand()) { - const [cmd, commandText] = this._getSlashCommand(); + const [cmd, args, commandText] = this._getSlashCommand(); if (cmd) { - shouldSend = false; - this._runSlashCommand(cmd); + if (cmd.category === CommandCategories.messages) { + content = await this._runSlashCommand(cmd, args); + if (replyToEvent) { + addReplyToMessageContent(content, replyToEvent, this.props.permalinkCreator); + } + } else { + this._runSlashCommand(cmd, args); + shouldSend = false; + } } else { // ask the user if their unknown command should be sent as a message const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); @@ -371,11 +388,12 @@ export default class SendMessageComposer extends React.Component { this._sendQuickReaction(); } - const replyToEvent = this.props.replyToEvent; if (shouldSend) { const startTime = CountlyAnalytics.getTimestamp(); const {roomId} = this.props.room; - const content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent); + if (!content) { + content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent); + } // don't bother sending an empty message if (!content.body.trim()) return; From 1a7f9091b4fc41eac6d5cc1b71d1dbe61f5d7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 07:51:03 +0100 Subject: [PATCH 121/863] Animate icon size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 ++++++++++++ src/components/views/rooms/AuxPanel.tsx | 10 +++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 28d8d1e196..d5caee5e8b 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -36,10 +36,22 @@ limitations under the License. z-index: 3000; display: flex; + flex-direction: column; justify-content: center; align-items: center; } +@keyframes mx_RoomView_fileDropTarget_image_animation { + from {width: 0px;} + to {width: 32px;} +} + +.mx_RoomView_fileDropTarget_image { + animation: mx_RoomView_fileDropTarget_image_animation; + animation-duration: 0.5s; + margin-bottom: 16px; +} + .mx_RoomView_auxPanel { min-width: 0px; width: 100%; diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 59ea8e237a..b3ef8c3cc8 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -153,11 +153,11 @@ export default class AuxPanel extends React.Component { if (this.props.draggingFile) { fileDropTarget = (
-
- -
- { _t("Drop file here to upload") } -
+ + { _t("Drop file here to upload") }
); } From f0c26846c75559e5be013eb25ebe5f54b4f6e264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:11:58 +0100 Subject: [PATCH 122/863] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index d5caee5e8b..2c3fb2b32b 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -42,8 +42,8 @@ limitations under the License. } @keyframes mx_RoomView_fileDropTarget_image_animation { - from {width: 0px;} - to {width: 32px;} + from {width: 0px;} + to {width: 32px;} } .mx_RoomView_fileDropTarget_image { From 172cc01f7d3dcba08235a621cff118efe3e76d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:12:10 +0100 Subject: [PATCH 123/863] Add background animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 2c3fb2b32b..5870e107c6 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -20,6 +20,12 @@ limitations under the License. flex-direction: column; } + +@keyframes mx_RoomView_fileDropTarget_animation { + from {opacity: 0;} + to {opacity: 0.95;} +} + .mx_RoomView_fileDropTarget { min-width: 0px; width: 100%; @@ -30,7 +36,8 @@ limitations under the License. pointer-events: none; - background-color: $droptarget-bg-color; + background-color: $primary-bg-color; + opacity: 0.95; position: absolute; z-index: 3000; @@ -39,6 +46,9 @@ limitations under the License. flex-direction: column; justify-content: center; align-items: center; + + animation: mx_RoomView_fileDropTarget_animation; + animation-duration: 0.5s; } @keyframes mx_RoomView_fileDropTarget_image_animation { From 3e0558f4d97bc618cc8e6d71f411370e894d642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:12:38 +0100 Subject: [PATCH 124/863] Remove droptarget colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/dark/css/_dark.scss | 3 --- res/themes/light/css/_light.scss | 3 --- 2 files changed, 6 deletions(-) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index f6f415ce70..a878aa3cdd 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -42,9 +42,6 @@ $preview-bar-bg-color: $header-panel-bg-color; $groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82); $inverted-bg-color: $base-color; -// used by RoomDropTarget -$droptarget-bg-color: rgba(21,25,30,0.95); - // used by AddressSelector $selected-color: $room-highlight-color; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index ea7b0472e0..c92e491ca2 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -67,9 +67,6 @@ $groupFilterPanel-bg-color: rgba(232, 232, 232, 0.77); // used by RoomDirectory permissions $plinth-bg-color: $secondary-accent-color; -// used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.95); - // used by AddressSelector $selected-color: $secondary-accent-color; From 49ea83edb93dfa0e6f572aeac002adaaa0370fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:14:27 +0100 Subject: [PATCH 125/863] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5bbbdf60b5..9af8ccc172 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1381,7 +1381,6 @@ "Remove %(phone)s?": "Remove %(phone)s?", "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.", "Phone Number": "Phone Number", - "Drop File Here": "Drop File Here", "Drop file here to upload": "Drop file here to upload", "This user has not verified all of their sessions.": "This user has not verified all of their sessions.", "You have not verified this user.": "You have not verified this user.", From e90ae2ea7596bff850cf4014c1109f93234132b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:18:05 +0100 Subject: [PATCH 126/863] Delint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5870e107c6..5e8d84ff32 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -22,8 +22,12 @@ limitations under the License. @keyframes mx_RoomView_fileDropTarget_animation { - from {opacity: 0;} - to {opacity: 0.95;} + from { + opacity: 0; + } + to { + opacity: 0.95; + } } .mx_RoomView_fileDropTarget { @@ -52,8 +56,12 @@ limitations under the License. } @keyframes mx_RoomView_fileDropTarget_image_animation { - from {width: 0px;} - to {width: 32px;} + from { + width: 0px; + } + to { + width: 32px; + } } .mx_RoomView_fileDropTarget_image { From 819a0b013fda927bea3197e458741f4f4f85a271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:08:31 +0100 Subject: [PATCH 127/863] min-width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to allow the container to be smaller Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5e8d84ff32..28591ad7a4 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -140,6 +140,7 @@ limitations under the License. display: flex; flex-direction: column; width: 100%; + min-width: 0; } .mx_RoomView_body { From 0d6a9fce67d24f441a62e140a2a73f669b959cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:12:14 +0100 Subject: [PATCH 128/863] Remove weird styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- 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 ff09af454e..42eafe5bdc 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2058,7 +2058,7 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel}
From 11c5aa02d290739fff31bfa8365fe76562032594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:19:45 +0100 Subject: [PATCH 129/863] Remove mx_RoomView_container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 + res/css/structures/_RoomView.scss | 8 ------- src/components/structures/RoomView.tsx | 30 ++++++++++++-------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 9597083e9c..5fa62e921d 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -20,6 +20,7 @@ limitations under the License. min-width: 0; height: 100%; justify-content: space-between; + min-height: 0; } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 28591ad7a4..b3dab5f992 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -135,14 +135,6 @@ limitations under the License. height: 50px; } -.mx_RoomView_container { - position: relative; //for .mx_RoomView_auxPanel_fullHeight - display: flex; - flex-direction: column; - width: 100%; - min-width: 0; -} - .mx_RoomView_body { display: flex; flex-direction: column; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 42eafe5bdc..be2f81a176 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2058,24 +2058,22 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel} -
-
- {topUnreadMessagesBar} - {jumpToBottom} - {messagePanel} - {searchResultsPanel} -
-
-
-
- {statusBar} -
-
- {previewBar} - {messageComposer} +
+ {topUnreadMessagesBar} + {jumpToBottom} + {messagePanel} + {searchResultsPanel}
+
+
+
+ {statusBar} +
+
+ {previewBar} + {messageComposer}
From 9a5ba072ba0f551dc618cdf8bfb7afba004d01fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:23:58 +0100 Subject: [PATCH 130/863] Fix auxPanel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- 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 be2f81a176..5b79f23e0b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2059,8 +2059,8 @@ export default class RoomView extends React.Component { />
- {auxPanel}
+ {auxPanel} {topUnreadMessagesBar} {jumpToBottom} {messagePanel} From 3bed37463fea1ff1a7c86ef5fe9a0a123e06008f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:38:05 +0100 Subject: [PATCH 131/863] Remove unnecessary code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 5fa62e921d..2d9ea2729c 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -19,7 +19,6 @@ limitations under the License. flex-direction: row; min-width: 0; height: 100%; - justify-content: space-between; min-height: 0; } From 3a643e5b9df189916d2a3b8162636e2d05a5e2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:46:54 +0100 Subject: [PATCH 132/863] Remove unnecessary changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 34 ++++++++++++------------------ res/css/structures/_RoomView.scss | 2 -- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 2d9ea2729c..8199121420 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -18,35 +18,27 @@ limitations under the License. display: flex; flex-direction: row; min-width: 0; - height: 100%; min-height: 0; + height: 100%; } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { - padding: 0 5px 5px 0px; + padding: 5px; + // margin left to not allow the handle to not encroach on the space for the scrollbar + margin-left: 8px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel - .mx_RightPanel_ResizeHandle { - width: 9px; - } - &:hover .mx_RightPanel_ResizeHandle { - &::before { - position: absolute; - left: 6px; - top: 50%; - transform: translate(0, -50%); + // Need to use important to override element style attributes + // set by re-resizable + top: 50% !important; + transform: translate(0, -50%); - height: 64px; - width: 4px; - border-radius: 4px; + height: 64px !important; // to match width of the ones on roomlist + width: 4px !important; + border-radius: 4px !important; - content: ' '; - - background-color: $primary-fg-color; - opacity: 0.8; - - margin-left: -10px; - } + background-color: $primary-fg-color; + opacity: 0.8; } } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index b3dab5f992..26382b55e8 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -144,8 +144,6 @@ limitations under the License. .mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner { order: 2; } - - margin-right: 10px; } .mx_RoomView_body .mx_RoomView_timeline { From 414f18b19f1c33d4cfaff8fa0440130b76d86dcf Mon Sep 17 00:00:00 2001 From: "@a2sc:matrix.org" Date: Fri, 26 Feb 2021 18:01:24 +0000 Subject: [PATCH 133/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 3f657e105b..269fef0da1 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -16,7 +16,7 @@ "Deops user with given id": "Setzt das Berechtigungslevel des/der Benutzer:in mit der angegebenen ID zurück", "Invites user with given id to current room": "Lädt den/die Benutzer:in mit der angegebenen ID in den aktuellen Raum ein", "Kicks user with given id": "Benutzer:in mit der angegebenen ID kicken", - "Changes your display nickname": "Ändert deinen angezeigten Nicknamen", + "Changes your display nickname": "Ändert deinen Anzeigenamen", "Change Password": "Passwort ändern", "Searches DuckDuckGo for results": "Verwendet DuckDuckGo für Suchergebnisse", "Commands": "Kommandos", @@ -115,7 +115,7 @@ "You are already in a call.": "Du bist bereits in einem Gespräch.", "You cannot place a call with yourself.": "Du kannst keinen Anruf mit dir selbst starten.", "You cannot place VoIP calls in this browser.": "VoIP-Gespräche werden von diesem Browser nicht unterstützt.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Deine E-Mail-Adresse scheint nicht mit einer Matrix-ID auf diesem Home-Server verbunden zu sein.", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Deine E-Mail-Adresse scheint nicht mit einer Matrix-ID auf diesem Homeserver verbunden zu sein.", "Sun": "So", "Mon": "Mo", "Tue": "Di", @@ -636,7 +636,7 @@ "Which officially provided instance you are using, if any": "Welche offiziell angebotene Instanz du nutzt, wenn überhaupt eine", "In reply to ": "Als Antwort auf ", "This room is not public. You will not be able to rejoin without an invite.": "Dies ist kein öffentlicher Raum. Du wirst diesen nicht ohne Einladung wieder beitreten können.", - "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s änderte den Anzeigenamen auf %(displayName)s.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s hat den Anzeigenamen zu %(displayName)s geändert.", "Failed to set direct chat tag": "Fehler beim Setzen der Direkt-Chat-Markierung", "Failed to remove tag %(tagName)s from room": "Entfernen der Raum-Kennzeichnung %(tagName)s fehlgeschlagen", "Failed to add tag %(tagName)s to room": "Fehler beim Hinzufügen des \"%(tagName)s\"-Tags an dem Raum", @@ -834,7 +834,7 @@ "This event could not be displayed": "Dieses Ereignis konnte nicht angezeigt werden", "A call is currently being placed!": "Ein Anruf wurde schon gestartet!", "Permission Required": "Berechtigung benötigt", - "You do not have permission to start a conference call in this room": "Du hast keine Berechtigung um ein Konferenzgespräch in diesem Raum zu starten", + "You do not have permission to start a conference call in this room": "Du hast keine Berechtigung ein Konferenzgespräch in diesem Raum zu starten", "Failed to remove widget": "Widget konnte nicht entfernt werden", "An error ocurred whilst trying to remove the widget from the room": "Ein Fehler trat auf während versucht wurde, das Widget aus diesem Raum zu entfernen", "System Alerts": "System-Benachrichtigung", @@ -2944,8 +2944,8 @@ "Homeserver": "Heimserver", "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Element with an existing Matrix account on a different homeserver.": "Du kannst in den benutzerdefinierten Serveroptionen eine andere Heimserver-URL angeben, um dich bei anderen Matrixservern anzumelden.", "Server Options": "Servereinstellungen", - "No other application is using the webcam": "Keine andere Anwendung auf die Webcam zugreift", - "Permission is granted to use the webcam": "Auf die Webcam zugegriffen werden darf", + "No other application is using the webcam": "keine andere Anwendung auf die Webcam zugreift", + "Permission is granted to use the webcam": "auf die Webcam zugegriffen werden darf", "A microphone and webcam are plugged in and set up correctly": "Mikrofon und Webcam eingesteckt und richtig eingerichtet sind", "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Der Anruf ist fehlgeschlagen weil nicht auf das Mikrofon zugegriffen werden konnte. Stelle sicher, dass das Mikrofon richtig eingesteckt und eingerichtet ist.", "Call failed because no webcam or microphone could not be accessed. Check that:": "Der Anruf ist fehlgeschlagen weil nicht auf das Mikrofon oder die Webcam zugegriffen werden konnte. Stelle sicher, dass:", From d83b935fc0f80bf9460b94254a54eb2cf0e52a63 Mon Sep 17 00:00:00 2001 From: libexus Date: Thu, 25 Feb 2021 07:30:54 +0000 Subject: [PATCH 134/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 269fef0da1..ff754e7d7e 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -181,7 +181,7 @@ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s hat die Verbannung von %(targetName)s aufgehoben.", "Usage": "Verwendung", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s hat die Einladung für %(targetName)s zurückgezogen.", - "You need to be able to invite users to do that.": "Du brauchst die Berechtigung Benutzer:innen einzuladen haben, um diese Aktion ausführen zu können.", + "You need to be able to invite users to do that.": "Du musst die Berechtigung \"Benutzer:innen einladen\" haben, um diese Aktion ausführen zu können.", "You need to be logged in.": "Du musst angemeldet sein.", "There are no visible files in this room": "Es gibt keine sichtbaren Dateien in diesem Raum", "Connectivity to the server has been lost.": "Verbindung zum Server wurde unterbrochen.", @@ -1636,7 +1636,7 @@ "Use Single Sign On to continue": "Single-Sign-On zum Fortfahren nutzen", "Confirm adding this email address by using Single Sign On to prove your identity.": "Bestätige die hinzugefügte E-Mail-Adresse mit Single Sign-On, um deine Identität nachzuweisen.", "Single Sign On": "Single Sign-On", - "Confirm adding email": "Bestätige hinzugefügte E-Mail-Addresse", + "Confirm adding email": "Hinzugefügte E-Mail-Addresse bestätigen", "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bestätige die hinzugefügte Telefonnummer, indem du deine Identität mittels Single Sign-On nachweist.", "Click the button below to confirm adding this phone number.": "Klicke unten die Schaltfläche, um die hinzugefügte Telefonnummer zu bestätigen.", "If you cancel now, you won't complete your operation.": "Wenn du jetzt abbrichst, wirst du deinen Vorgang nicht fertigstellen.", @@ -3059,5 +3059,16 @@ "Cookie Policy": "Cookie-Richtlinie", "Learn more in our , and .": "Erfahren mehr in unserer , und .", "Failed to connect to your homeserver. Please close this dialog and try again.": "Verbindung zum Homeserver fehlgeschlagen. Bitte schließe diesen Dialog and versuche es erneut.", - "Abort": "Abbrechen" + "Abort": "Abbrechen", + "Upgrade to %(hostSignupBrand)s": "Zu %(hostSignupBrand)s upgraden", + "Edit Values": "Werte bearbeiten", + "Value in this room:": "Wert in diesem Raum:", + "Value:": "Wert:", + "Level": "Level", + "This UI does NOT check the types of the values. Use at your own risk.": "Diese Benutzeroberfläche prüft nicht auf richtige Datentypen. Benutzung auf eigene Gefahr.", + "Setting:": "Einstellung:", + "Value": "Wert", + "Setting ID": "Einstellungs-ID", + "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", + "Show chat effects (animations when receiving e.g. confetti)": "Animierte Chateffekte zeigen, wenn z.B. Konfetti-Emojis erhalten werden" } From de73cef7e61ea57e1750f246e0a9f7d9ef785472 Mon Sep 17 00:00:00 2001 From: libexus Date: Fri, 26 Feb 2021 18:02:04 +0000 Subject: [PATCH 135/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 ff754e7d7e..29b86ac641 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -636,7 +636,7 @@ "Which officially provided instance you are using, if any": "Welche offiziell angebotene Instanz du nutzt, wenn überhaupt eine", "In reply to ": "Als Antwort auf ", "This room is not public. You will not be able to rejoin without an invite.": "Dies ist kein öffentlicher Raum. Du wirst diesen nicht ohne Einladung wieder beitreten können.", - "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s hat den Anzeigenamen zu %(displayName)s geändert.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s hat den Anzeigenamen auf %(displayName)s geändert.", "Failed to set direct chat tag": "Fehler beim Setzen der Direkt-Chat-Markierung", "Failed to remove tag %(tagName)s from room": "Entfernen der Raum-Kennzeichnung %(tagName)s fehlgeschlagen", "Failed to add tag %(tagName)s to room": "Fehler beim Hinzufügen des \"%(tagName)s\"-Tags an dem Raum", From e3065f5a02533c38f0a7b770c519c5314d27880d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 26 Feb 2021 17:09:54 -0500 Subject: [PATCH 136/863] Support sending invite reasons with MultiInviter Signed-off-by: Robin Townsend --- src/utils/MultiInviter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js index 7d1c900360..63d3942b37 100644 --- a/src/utils/MultiInviter.js +++ b/src/utils/MultiInviter.js @@ -53,13 +53,15 @@ export default class MultiInviter { * instance of the class. * * @param {array} addrs Array of addresses to invite + * @param {string} reason Reason for inviting (optional) * @returns {Promise} Resolved when all invitations in the queue are complete */ - invite(addrs) { + invite(addrs, reason) { if (this.addrs.length > 0) { throw new Error("Already inviting/invited"); } this.addrs.push(...addrs); + this.reason = reason; for (const addr of this.addrs) { if (getAddressType(addr) === null) { @@ -123,7 +125,7 @@ export default class MultiInviter { } } - return MatrixClientPeg.get().invite(roomId, addr); + return MatrixClientPeg.get().invite(roomId, addr, undefined, this.reason); } else { throw new Error('Unsupported address'); } From c25a8b70fa051afe102a301d146448f44cd51c3d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 26 Feb 2021 17:10:20 -0500 Subject: [PATCH 137/863] Support sending invite reasons with /invite command Signed-off-by: Robin Townsend --- src/SlashCommands.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 6b5f261374..aedcf7af8c 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -441,15 +441,14 @@ export const Commands = [ }), new Command({ command: 'invite', - args: '', + args: ' []', description: _td('Invites user with given id to current room'), runFn: function(roomId, args) { if (args) { - const matches = args.match(/^(\S+)$/); - if (matches) { + const [address, reason] = args.split(/\s+(.+)/); + if (address) { // We use a MultiInviter to re-use the invite logic, even though // we're only inviting one user. - const address = matches[1]; // If we need an identity server but don't have one, things // get a bit more complex here, but we try to show something // meaningful. @@ -490,7 +489,7 @@ export const Commands = [ } const inviter = new MultiInviter(roomId); return success(prom.then(() => { - return inviter.invite([address]); + return inviter.invite([address], reason); }).then(() => { if (inviter.getCompletionState(address) !== "invited") { throw new Error(inviter.getErrorText(address)); From f1fabd831c172b7f2fadd70de91a3aa7358b493b Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 25 Feb 2021 16:59:27 -0500 Subject: [PATCH 138/863] Add /spoiler command As a temporary measure until we have an extensible Markdown parser. Signed-off-by: Robin Townsend --- src/SlashCommands.tsx | 12 ++++++++++++ src/i18n/strings/en_EN.json | 1 + 2 files changed, 13 insertions(+) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 6b5f261374..7699cc3c25 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -154,6 +154,18 @@ function success(promise?: Promise) { */ export const Commands = [ + new Command({ + command: 'spoiler', + args: '', + description: _td('Sends the given message as a spoiler'), + runFn: function(roomId, message) { + return success(MatrixClientPeg.get().sendHtmlMessage( + roomId, message, + `${message}`, + )); + }, + category: CommandCategories.messages, + }), new Command({ command: 'shrug', args: '', diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0be7e6e02b..85f03158bd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -415,6 +415,7 @@ "Other": "Other", "Command error": "Command error", "Usage": "Usage", + "Sends the given message as a spoiler": "Sends the given message as a spoiler", "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message", "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message", "Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message": "Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message", From 905f5300f49e2770d77d8d3e9c86484735bcbdaa Mon Sep 17 00:00:00 2001 From: Nikita Epifanov Date: Thu, 25 Feb 2021 11:01:31 +0000 Subject: [PATCH 139/863] Translated using Weblate (Russian) Currently translated at 99.4% (2765 of 2780 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 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index a72eef2c58..65aeaf82d9 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -3057,5 +3057,20 @@ "Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "Ваш домашний сервер был недоступен, и мы не смогли войти в систему. Пожалуйста, попробуйте снова через пару минут. Если ситуация по-прежнему не меняется, обратитесь к администратору домашнего сервера за дополнительной информацией.", "Try again": "Попробовать ещё раз", "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Мы попросили браузер запомнить, какой домашний сервер вы используете для входа в систему, но, к сожалению, ваш браузер забыл об этом. Перейдите на страницу входа и попробуйте ещё раз.", - "We couldn't log you in": "Нам не удалось войти в систему" + "We couldn't log you in": "Нам не удалось войти в систему", + "Upgrade to %(hostSignupBrand)s": "Перейти на %(hostSignupBrand)s", + "Edit Values": "Изменить значения", + "Value in this room:": "Значение в этой комнате:", + "Value:": "Значение:", + "Setting:": "Настройки:", + "Setting ID": "ID настроек", + "Save setting values": "Сохранить значения настроек", + "Settable at room": "Устанавливается для комнаты", + "Settable at global": "Устанавливается на глобальном уровне", + "Level": "Уровень", + "This UI does NOT check the types of the values. Use at your own risk.": "Этот пользовательский интерфейс НЕ проверяет типы значений. Используйте на свой риск.", + "Value in this room": "Значение в этой комнате", + "Value": "Значение", + "Failed to save settings": "Не удалось сохранить настройки", + "Show chat effects (animations when receiving e.g. confetti)": "Показать эффекты чата (анимация при получении, например, конфетти)" } From 4b771b0634594dcc8205fcc19ac49e107079c24f Mon Sep 17 00:00:00 2001 From: libexus Date: Sat, 27 Feb 2021 14:58:24 +0000 Subject: [PATCH 140/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 29b86ac641..e2e2e0718a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1569,7 +1569,7 @@ "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s hat die alternative Adresse %(addresses)s für diesen Raum entfernt.", "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s hat die alternative Adresse für diesen Raum geändert.", "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s hat die Haupt- und Alternativadressen für diesen Raum geändert.", - "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s entfernte die Ausschluss-Regel für Nutzer!nnen, die %(glob)s entsprechen", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s entfernte die Ausschluss-Regel für Benutzer:innen, die %(glob)s entsprechen", "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s entfernte die Ausschluss-Regel für Räume, die %(glob)s entsprechen", "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s entfernte die Ausschluss-Regel für Server, die %(glob)s entsprechen", "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s entfernte die Ausschluss-Regel, die %(glob)s entspricht", @@ -2567,7 +2567,7 @@ "See when the name changes in this room": "Sehen wenn sich der Name in diesem Raum ändert", "Change the name of your active room": "Den Namen deines aktiven Raums ändern", "See when the name changes in your active room": "Sehen wenn der Name sich in deinem aktiven Raum ändert", - "Change the avatar of this room": "Avatar von diesem Raum ändern", + "Change the avatar of this room": "Icon von diesem Raum ändern", "See when the avatar changes in this room": "Sehen wenn der Avatar sich in diesem Raum ändert", "Change the avatar of your active room": "Den Avatar deines aktiven Raums ändern", "See when the avatar changes in your active room": "Sehen wenn ein Avatar in deinem aktiven Raum geändert wird", @@ -3036,7 +3036,7 @@ "Converts the room to a DM": "Wandelt den Raum zu Direktnachricht um", "Something went wrong in confirming your identity. Cancel and try again.": "Bei der Bestätigung deiner Identität ist ein Fehler aufgetreten. Abbrechen und erneut versuchen.", "Use app": "App verwenden", - "Element Web is experimental on mobile. For a better experience and the latest features, use our free native app.": "Element Web ist experimentell auf mobilen Endgeräten. Für eine bessere Erfahrung und die neuesten Erweiterungen, nutze unsere freie, native App.", + "Element Web is experimental on mobile. For a better experience and the latest features, use our free native app.": "Element Web ist auf mobilen Endgeräten experimentell. Für eine bessere Erfahrung und die neuesten Features, nutze unsere freie, native App.", "Use app for a better experience": "Nutze die App für eine bessere Erfahrung", "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Wir haben deinen Browser gebeten, sich zu merken, bei welchem Homeserver du dich anmeldest, aber dein Browser hat dies leider vergessen. Gehe zur Anmeldeseite und versuche es erneut.", "Show stickers button": "Sticker-Schaltfläche anzeigen", From 844ed6b0b9c5e8a99aad99192e9506b047d72904 Mon Sep 17 00:00:00 2001 From: "@a2sc:matrix.org" Date: Fri, 26 Feb 2021 18:04:45 +0000 Subject: [PATCH 141/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index e2e2e0718a..b615cda081 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -636,7 +636,7 @@ "Which officially provided instance you are using, if any": "Welche offiziell angebotene Instanz du nutzt, wenn überhaupt eine", "In reply to ": "Als Antwort auf ", "This room is not public. You will not be able to rejoin without an invite.": "Dies ist kein öffentlicher Raum. Du wirst diesen nicht ohne Einladung wieder beitreten können.", - "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s hat den Anzeigenamen auf %(displayName)s geändert.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s hat den Anzeigenamen zu %(displayName)s geändert.", "Failed to set direct chat tag": "Fehler beim Setzen der Direkt-Chat-Markierung", "Failed to remove tag %(tagName)s from room": "Entfernen der Raum-Kennzeichnung %(tagName)s fehlgeschlagen", "Failed to add tag %(tagName)s to room": "Fehler beim Hinzufügen des \"%(tagName)s\"-Tags an dem Raum", @@ -2664,7 +2664,7 @@ "Fill Screen": "Bildschirm ausfüllen", "Voice Call": "Sprachanruf", "Video Call": "Videoanruf", - "Remain on your screen while running": "Bleiben Sie auf Ihrem Bildschirm während der Ausführung von", + "Remain on your screen while running": "Bleib auf deinem Bildschirm während der Ausführung von", "Remain on your screen when viewing another room, when running": "Bleiben Sie auf Ihrem Bildschirm, während Sie einen anderen Raum betrachten, wenn Sie ausführen", "Zimbabwe": "Simbabwe", "Zambia": "Sambia", From f29a8ef0f707a9c9a9168b7f1177dda771a802c9 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Sun, 28 Feb 2021 20:12:36 +1300 Subject: [PATCH 142/863] Handle shift + letter combos --- src/KeyBindingsManager.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index ef5084c16c..e26950b862 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -264,8 +264,17 @@ const autocompleteBindings = (): KeyBinding[] => { * Note, this method is only exported for testing. */ export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo: KeyCombo, onMac: boolean): boolean { - if (combo.key !== undefined && ev.key !== combo.key) { - return false; + if (combo.key !== undefined) { + // When shift is pressed, letters are returned as upper case chars. In this case do a lower case comparison. + // This works for letter combos such as shift + U as well for none letter combos such as shift + Escape. + // If shift is not pressed, the toLowerCase conversion can be avoided. + if (ev.shiftKey) { + if (ev.key.toLowerCase() !== combo.key.toLowerCase()) { + return false; + } + } else if (ev.key !== combo.key) { + return false; + } } const comboCtrl = combo.ctrlKey ?? false; From 32ec8b7dc84af60811ef2d1155f4839fd3f79285 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Sun, 28 Feb 2021 20:13:34 +1300 Subject: [PATCH 143/863] Add key bindings for RoomList, Room and Navigation --- src/KeyBindingsManager.ts | 244 +++++++++++++++++++++ src/components/structures/LoggedInView.tsx | 162 +++++++------- src/components/structures/RoomSearch.tsx | 33 +-- src/components/structures/RoomView.tsx | 32 ++- src/components/views/rooms/RoomSublist.tsx | 12 +- 5 files changed, 365 insertions(+), 118 deletions(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index e26950b862..b969982bda 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -6,6 +6,12 @@ export enum KeyBindingContext { MessageComposer = 'MessageComposer', /** Key bindings for text editing autocompletion */ AutoComplete = 'AutoComplete', + /** Left room list sidebar */ + RoomList = 'RoomList', + /** Current room view */ + Room = 'Room', + /** Shortcuts to navigate do various menus / dialogs / screens */ + Navigation = 'Navigation', } export enum KeyAction { @@ -51,6 +57,59 @@ export enum KeyAction { AutocompletePrevSelection = 'AutocompletePrevSelection', /** Move to the next autocomplete selection */ AutocompleteNextSelection = 'AutocompleteNextSelection', + + // Room list + + /** Clear room list filter field */ + RoomListClearSearch = 'RoomListClearSearch', + /** Navigate up/down in the room list */ + RoomListPrevRoom = 'RoomListPrevRoom', + /** Navigate down in the room list */ + RoomListNextRoom = 'RoomListNextRoom', + /** Select room from the room list */ + RoomListSelectRoom = 'RoomListSelectRoom', + /** Collapse room list section */ + RoomListCollapseSection = 'RoomListCollapseSection', + /** Expand room list section, if already expanded, jump to first room in the selection */ + RoomListExpandSection = 'RoomListExpandSection', + + // Room + + /** Jump to room search */ + RoomFocusRoomSearch = 'RoomFocusRoomSearch', + /** Scroll up in the timeline */ + RoomScrollUp = 'RoomScrollUp', + /** Scroll down in the timeline */ + RoomScrollDown = 'RoomScrollDown', + /** Dismiss read marker and jump to bottom */ + RoomDismissReadMarker = 'RoomDismissReadMarker', + /* Upload a file */ + RoomUploadFile = 'RoomUploadFile', + /* Search (must be enabled) */ + RoomSearch = 'RoomSearch', + /* Jump to the first (downloaded) message in the room */ + RoomJumpToFirstMessage = 'RoomJumpToFirstMessage', + /* Jump to the latest message in the room */ + RoomJumpToLatestMessage = 'RoomJumpToLatestMessage', + + // Navigation + + /** Toggle the room side panel */ + NavToggleRoomSidePanel = 'NavToggleRoomSidePanel', + /** Toggle the user menu */ + NavToggleUserMenu = 'NavToggleUserMenu', + /* Toggle the short cut help dialog */ + NavToggleShortCutDialog = 'NavToggleShortCutDialog', + /* Got to the Element home screen */ + NavGoToHome = 'NavGoToHome', + /* Select prev room */ + NavSelectPrevRoom = 'NavSelectPrevRoom', + /* Select next room */ + NavSelectNextRoom = 'NavSelectNextRoom', + /* Select prev room with unread messages*/ + NavSelectPrevUnreadRoom = 'NavSelectPrevUnreadRoom', + /* Select next room with unread messages*/ + NavSelectNextUnreadRoom = 'NavSelectNextUnreadRoom', } /** @@ -255,6 +314,188 @@ const autocompleteBindings = (): KeyBinding[] => { key: Key.ARROW_DOWN, }, }, + ]; +} + +const roomListBindings = (): KeyBinding[] => { + return [ + { + action: KeyAction.RoomListClearSearch, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: KeyAction.RoomListPrevRoom, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: KeyAction.RoomListNextRoom, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + { + action: KeyAction.RoomListSelectRoom, + keyCombo: { + key: Key.ENTER, + }, + }, + { + action: KeyAction.RoomListCollapseSection, + keyCombo: { + key: Key.ARROW_LEFT, + }, + }, + { + action: KeyAction.RoomListExpandSection, + keyCombo: { + key: Key.ARROW_RIGHT, + }, + }, + ]; +} + +const roomBindings = (): KeyBinding[] => { + const bindings = [ + { + action: KeyAction.RoomFocusRoomSearch, + keyCombo: { + key: Key.K, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.RoomScrollUp, + keyCombo: { + key: Key.PAGE_UP, + }, + }, + { + action: KeyAction.RoomScrollDown, + keyCombo: { + key: Key.PAGE_DOWN, + }, + }, + { + action: KeyAction.RoomDismissReadMarker, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: KeyAction.RoomUploadFile, + keyCombo: { + key: Key.U, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: KeyAction.RoomJumpToFirstMessage, + keyCombo: { + key: Key.HOME, + ctrlKey: true, + }, + }, + { + action: KeyAction.RoomJumpToLatestMessage, + keyCombo: { + key: Key.END, + ctrlKey: true, + }, + }, + ]; + + if (SettingsStore.getValue('ctrlFForSearch')) { + bindings.push({ + action: KeyAction.RoomSearch, + keyCombo: { + key: Key.F, + ctrlOrCmd: true, + }, + }); + } + + return bindings; +} + +const navigationBindings = (): KeyBinding[] => { + return [ + { + action: KeyAction.NavToggleRoomSidePanel, + keyCombo: { + key: Key.PERIOD, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.NavToggleUserMenu, + // Ideally this would be CTRL+P for "Profile", but that's + // taken by the print dialog. CTRL+I for "Information" + // was previously chosen but conflicted with italics in + // composer, so CTRL+` it is + keyCombo: { + key: Key.BACKTICK, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.NavToggleShortCutDialog, + keyCombo: { + key: Key.SLASH, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.NavToggleShortCutDialog, + keyCombo: { + key: Key.SLASH, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: KeyAction.NavGoToHome, + keyCombo: { + key: Key.H, + ctrlOrCmd: true, + altKey: true, + }, + }, + + { + action: KeyAction.NavSelectPrevRoom, + keyCombo: { + key: Key.ARROW_UP, + altKey: true, + }, + }, + { + action: KeyAction.NavSelectNextRoom, + keyCombo: { + key: Key.ARROW_DOWN, + altKey: true, + }, + }, + { + action: KeyAction.NavSelectPrevUnreadRoom, + keyCombo: { + key: Key.ARROW_UP, + altKey: true, + shiftKey: true, + }, + }, + { + action: KeyAction.NavSelectNextUnreadRoom, + keyCombo: { + key: Key.ARROW_DOWN, + altKey: true, + shiftKey: true, + }, + }, ] } @@ -323,6 +564,9 @@ export class KeyBindingsManager { contextBindings: Record = { [KeyBindingContext.MessageComposer]: messageComposerBindings, [KeyBindingContext.AutoComplete]: autocompleteBindings, + [KeyBindingContext.RoomList]: roomListBindings, + [KeyBindingContext.Room]: roomBindings, + [KeyBindingContext.Navigation]: navigationBindings, }; /** diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index c76cd7cee7..dd8bc1f3db 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, isMac} from '../../Keyboard'; +import {Key} from '../../Keyboard'; import PageTypes from '../../PageTypes'; import CallMediaHandler from '../../CallMediaHandler'; import { fixupColorFonts } from '../../utils/FontManager'; @@ -55,6 +55,7 @@ import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import Modal from "../../Modal"; import { ICollapseConfig } from "../../resizer/distributors/collapse"; import HostSignupContainer from '../views/host_signup/HostSignupContainer'; +import { getKeyBindingsManager, KeyAction, KeyBindingContext } from '../../KeyBindingsManager'; // 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. @@ -399,86 +400,54 @@ class LoggedInView extends React.Component { _onKeyDown = (ev) => { let handled = false; - 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: - case Key.PAGE_DOWN: - if (!hasModifier && !isModifier) { - this._onScrollKeyPressed(ev); - handled = true; - } + const roomAction = getKeyBindingsManager().getAction(KeyBindingContext.Room, ev); + switch (roomAction) { + case KeyAction.RoomFocusRoomSearch: + dis.dispatch({ + action: 'focus_room_filter', + }); + handled = true; break; + case KeyAction.RoomScrollUp: + case KeyAction.RoomScrollDown: + case KeyAction.RoomJumpToFirstMessage: + case KeyAction.RoomJumpToLatestMessage: + this._onScrollKeyPressed(ev); + handled = true; + break; + case KeyAction.RoomSearch: + dis.dispatch({ + action: 'focus_search', + }); + handled = true; + break; + } + if (handled) { + ev.stopPropagation(); + ev.preventDefault(); + return; + } - case Key.HOME: - case Key.END: - if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { - this._onScrollKeyPressed(ev); - handled = true; - } + const navAction = getKeyBindingsManager().getAction(KeyBindingContext.Navigation, ev); + switch (navAction) { + case KeyAction.NavToggleUserMenu: + dis.fire(Action.ToggleUserMenu); + handled = true; break; - case Key.K: - if (ctrlCmdOnly) { - dis.dispatch({ - action: 'focus_room_filter', - }); - handled = true; - } + case KeyAction.NavToggleShortCutDialog: + KeyboardShortcuts.toggleDialog(); + handled = true; break; - case Key.F: - if (ctrlCmdOnly && SettingsStore.getValue("ctrlFForSearch")) { - dis.dispatch({ - action: 'focus_search', - }); - handled = true; - } + case KeyAction.NavGoToHome: + dis.dispatch({ + action: 'view_home_page', + }); + Modal.closeCurrentModal("homeKeyboardShortcut"); + handled = true; break; - case Key.BACKTICK: - // Ideally this would be CTRL+P for "Profile", but that's - // taken by the print dialog. CTRL+I for "Information" - // was previously chosen but conflicted with italics in - // composer, so CTRL+` it is - - if (ctrlCmdOnly) { - dis.fire(Action.ToggleUserMenu); - handled = true; - } - break; - - case Key.SLASH: - if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev)) { - KeyboardShortcuts.toggleDialog(); - handled = true; - } - break; - - case Key.H: - if (ev.altKey && modKey) { - dis.dispatch({ - action: 'view_home_page', - }); - Modal.closeCurrentModal("homeKeyboardShortcut"); - handled = true; - } - break; - - case Key.ARROW_UP: - case Key.ARROW_DOWN: - if (ev.altKey && !ev.ctrlKey && !ev.metaKey) { - dis.dispatch({ - action: Action.ViewRoomDelta, - delta: ev.key === Key.ARROW_UP ? -1 : 1, - unread: ev.shiftKey, - }); - handled = true; - } - break; - - case Key.PERIOD: - if (ctrlCmdOnly && (this.props.page_type === "room_view" || this.props.page_type === "group_view")) { + case KeyAction.NavToggleRoomSidePanel: + if (this.props.page_type === "room_view" || this.props.page_type === "group_view") { dis.dispatch({ action: Action.ToggleRightPanel, type: this.props.page_type === "room_view" ? "room" : "group", @@ -486,16 +455,47 @@ class LoggedInView extends React.Component { handled = true; } break; - - default: - // if we do not have a handler for it, pass it to the platform which might - handled = PlatformPeg.get().onKeyDown(ev); + case KeyAction.NavSelectPrevRoom: + dis.dispatch({ + action: Action.ViewRoomDelta, + delta: -1, + unread: false, + }); + handled = true; + break; + case KeyAction.NavSelectNextRoom: + dis.dispatch({ + action: Action.ViewRoomDelta, + delta: 1, + unread: false, + }); + handled = true; + break; + case KeyAction.NavSelectPrevUnreadRoom: + dis.dispatch({ + action: Action.ViewRoomDelta, + delta: -1, + unread: true, + }); + break; + case KeyAction.NavSelectNextUnreadRoom: + dis.dispatch({ + action: Action.ViewRoomDelta, + delta: 1, + unread: true, + }); + break; } - + // if we do not have a handler for it, pass it to the platform which might + handled = PlatformPeg.get().onKeyDown(ev); if (handled) { ev.stopPropagation(); ev.preventDefault(); - } else if (!isModifier && !ev.altKey && !ev.ctrlKey && !ev.metaKey) { + return; + } + + const isModifier = ev.key === Key.ALT || ev.key === Key.CONTROL || ev.key === Key.META || ev.key === Key.SHIFT; + if (!isModifier && !ev.altKey && !ev.ctrlKey && !ev.metaKey) { // The above condition is crafted to _allow_ characters with Shift // already pressed (but not the Shift key down itself). diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index a64e40bc65..2e900d2f0e 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -20,11 +20,11 @@ import classNames from "classnames"; import defaultDispatcher from "../../dispatcher/dispatcher"; import { _t } from "../../languageHandler"; import { ActionPayload } from "../../dispatcher/payloads"; -import { Key } from "../../Keyboard"; import AccessibleButton from "../views/elements/AccessibleButton"; import { Action } from "../../dispatcher/actions"; import RoomListStore from "../../stores/room-list/RoomListStore"; import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition"; +import { getKeyBindingsManager, KeyAction, KeyBindingContext } from "../../KeyBindingsManager"; interface IProps { isMinimized: boolean; @@ -106,18 +106,25 @@ export default class RoomSearch extends React.PureComponent { }; private onKeyDown = (ev: React.KeyboardEvent) => { - if (ev.key === Key.ESCAPE) { - this.clearInput(); - defaultDispatcher.fire(Action.FocusComposer); - } else if (ev.key === Key.ARROW_UP || ev.key === Key.ARROW_DOWN) { - this.props.onVerticalArrow(ev); - } else if (ev.key === Key.ENTER) { - const shouldClear = this.props.onEnter(ev); - if (shouldClear) { - // wrap in set immediate to delay it so that we don't clear the filter & then change room - setImmediate(() => { - this.clearInput(); - }); + const action = getKeyBindingsManager().getAction(KeyBindingContext.RoomList, ev); + switch (action) { + case KeyAction.RoomListClearSearch: + this.clearInput(); + defaultDispatcher.fire(Action.FocusComposer); + break; + case KeyAction.RoomListNextRoom: + case KeyAction.RoomListPrevRoom: + this.props.onVerticalArrow(ev); + break; + case KeyAction.RoomListSelectRoom: { + const shouldClear = this.props.onEnter(ev); + if (shouldClear) { + // wrap in set immediate to delay it so that we don't clear the filter & then change room + setImmediate(() => { + this.clearInput(); + }); + } + break; } } }; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 7b72b7f33f..c09f1f7c45 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -41,7 +41,6 @@ import rateLimitedFunc from '../../ratelimitedfunc'; import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; import eventSearch, { searchPagination } from '../../Searching'; -import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent, Key } from '../../Keyboard'; import MainSplit from './MainSplit'; import RightPanel from './RightPanel'; import RoomViewStore from '../../stores/RoomViewStore'; @@ -79,6 +78,7 @@ import Notifier from "../../Notifier"; import { showToast as showNotificationsToast } from "../../toasts/DesktopNotificationsToast"; import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore"; import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore"; +import { getKeyBindingsManager, KeyAction, KeyBindingContext } from '../../KeyBindingsManager'; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -661,26 +661,20 @@ export default class RoomView extends React.Component { private onReactKeyDown = ev => { let handled = false; - switch (ev.key) { - case Key.ESCAPE: - if (!ev.altKey && !ev.ctrlKey && !ev.shiftKey && !ev.metaKey) { - this.messagePanel.forgetReadMarker(); - this.jumpToLiveTimeline(); - handled = true; - } + const action = getKeyBindingsManager().getAction(KeyBindingContext.Room, ev); + switch (action) { + case KeyAction.RoomDismissReadMarker: + this.messagePanel.forgetReadMarker(); + this.jumpToLiveTimeline(); + handled = true; break; - case Key.PAGE_UP: - if (!ev.altKey && !ev.ctrlKey && ev.shiftKey && !ev.metaKey) { - this.jumpToReadMarker(); - handled = true; - } + case KeyAction.RoomScrollUp: + this.jumpToReadMarker(); + handled = true; break; - case Key.U: // Mac returns lowercase - case Key.U.toUpperCase(): - if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) { - dis.dispatch({ action: "upload_file" }, true); - handled = true; - } + case KeyAction.RoomUploadFile: + dis.dispatch({ action: "upload_file" }, true); + handled = true; break; } diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index a2574bf60c..c0919090b0 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -51,6 +51,7 @@ import { objectExcluding, objectHasDiff } from "../../../utils/objects"; import TemporaryTile from "./TemporaryTile"; import { ListNotificationState } from "../../../stores/notifications/ListNotificationState"; import IconizedContextMenu from "../context_menus/IconizedContextMenu"; +import { getKeyBindingsManager, KeyAction, KeyBindingContext } from "../../../KeyBindingsManager"; const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS @@ -470,18 +471,19 @@ export default class RoomSublist extends React.Component { }; private onHeaderKeyDown = (ev: React.KeyboardEvent) => { - switch (ev.key) { - case Key.ARROW_LEFT: + const action = getKeyBindingsManager().getAction(KeyBindingContext.RoomList, ev); + switch (action) { + case KeyAction.RoomListCollapseSection: ev.stopPropagation(); if (this.state.isExpanded) { - // On ARROW_LEFT collapse the room sublist if it isn't already + // Collapse the room sublist if it isn't already this.toggleCollapsed(); } break; - case Key.ARROW_RIGHT: { + case KeyAction.RoomListExpandSection: { ev.stopPropagation(); if (!this.state.isExpanded) { - // On ARROW_RIGHT expand the room sublist if it isn't already + // Expand the room sublist if it isn't already this.toggleCollapsed(); } else if (this.sublistRef.current) { // otherwise focus the first room From 94fbd7c9b127faaab1c668a101b6b0506798d767 Mon Sep 17 00:00:00 2001 From: "@a2sc:matrix.org" Date: Sat, 27 Feb 2021 14:58:41 +0000 Subject: [PATCH 144/863] Translated using Weblate (German) Currently translated at 99.2% (2759 of 2780 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 b615cda081..8a07b6cd9f 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -3036,7 +3036,7 @@ "Converts the room to a DM": "Wandelt den Raum zu Direktnachricht um", "Something went wrong in confirming your identity. Cancel and try again.": "Bei der Bestätigung deiner Identität ist ein Fehler aufgetreten. Abbrechen und erneut versuchen.", "Use app": "App verwenden", - "Element Web is experimental on mobile. For a better experience and the latest features, use our free native app.": "Element Web ist auf mobilen Endgeräten experimentell. Für eine bessere Erfahrung und die neuesten Features, nutze unsere freie, native App.", + "Element Web is experimental on mobile. For a better experience and the latest features, use our free native app.": "Element Web ist auf mobilen Endgeräten experimentell. Für eine bessere Erfahrung und die neuesten Erweiterungen, nutze unsere freie, native App.", "Use app for a better experience": "Nutze die App für eine bessere Erfahrung", "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Wir haben deinen Browser gebeten, sich zu merken, bei welchem Homeserver du dich anmeldest, aber dein Browser hat dies leider vergessen. Gehe zur Anmeldeseite und versuche es erneut.", "Show stickers button": "Sticker-Schaltfläche anzeigen", From d6d5455a11915a313b137b4a8f98bde762441117 Mon Sep 17 00:00:00 2001 From: Thibault Martin Date: Thu, 25 Feb 2021 17:17:39 +0000 Subject: [PATCH 145/863] Translated using Weblate (French) Currently translated at 99.2% (2758 of 2780 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 | 260 +++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index c942cae520..304599cd44 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1,6 +1,6 @@ { "Disinvite": "Désinviter", - "Displays action": "Affiche l'action", + "Displays action": "Affiche l’action", "Download %(text)s": "Télécharger %(text)s", "Emoji": "Émojis", "%(senderName)s ended the call.": "%(senderName)s a terminé l’appel.", @@ -32,14 +32,14 @@ "%(senderName)s banned %(targetName)s.": "%(senderName)s a banni %(targetName)s.", "Ban": "Bannir", "Banned users": "Utilisateurs bannis", - "Bans user with given id": "Bannit l'utilisateur à partir de son identifiant", + "Bans user with given id": "Bannit l’utilisateur à partir de son identifiant", "Call Timeout": "L’appel a dépassé le délai d'attente maximal", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Impossible de se connecter au serveur d'accueil en HTTP si l'URL dans la barre de votre explorateur est en HTTPS. Utilisez HTTPS ou activez le support des scripts non-vérifiés.", "Change Password": "Changer le mot de passe", "%(senderName)s changed their profile picture.": "%(senderName)s a changé son image de profil.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s a changé le rang de %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s a changé le nom du salon en %(roomName)s.", - "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s a changé le sujet du salon en \"%(topic)s\".", + "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s a changé le sujet du salon en « %(topic)s ».", "Changes your display nickname": "Change votre nom affiché", "Click here to fix": "Cliquer ici pour réparer", "Click to mute audio": "Cliquer pour couper le son", @@ -54,7 +54,7 @@ "Create Room": "Créer un salon", "Cryptography": "Chiffrement", "Current password": "Mot de passe actuel", - "/ddg is not a command": "/ddg n'est pas une commande", + "/ddg is not a command": "/ddg n’est pas une commande", "Deactivate Account": "Fermer le compte", "Decrypt %(text)s": "Déchiffrer %(text)s", "Deops user with given id": "Retire le rang d’opérateur d’un utilisateur à partir de son identifiant", @@ -66,7 +66,7 @@ "Failed to reject invite": "Échec du rejet de l'invitation", "Failed to reject invitation": "Échec du rejet de l'invitation", "Failed to send email": "Échec de l’envoi de l’e-mail", - "Failed to send request.": "Échec de l'envoi de la requête.", + "Failed to send request.": "Échec de l’envoi de la requête.", "Failed to set display name": "Échec de l'enregistrement du nom affiché", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s a accepté l’invitation de %(displayName)s.", "Access Token:": "Jeton d’accès :", @@ -101,17 +101,17 @@ "%(targetName)s joined the room.": "%(targetName)s a rejoint le salon.", "%(senderName)s kicked %(targetName)s.": "%(senderName)s a expulsé %(targetName)s.", "Kick": "Expulser", - "Kicks user with given id": "Expulse l'utilisateur à partir de son identifiant", + "Kicks user with given id": "Expulse l’utilisateur à partir de son identifiant", "Labs": "Labo", "Leave room": "Quitter le salon", "%(targetName)s left the room.": "%(targetName)s a quitté le salon.", "Logout": "Se déconnecter", "Low priority": "Priorité basse", - "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s a rendu l'historique visible à tous les membres du salon, depuis le moment où ils ont été invités.", - "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s a rendu l'historique visible à tous les membres du salon, depuis le moment où ils ont rejoint.", - "%(senderName)s made future room history visible to all room members.": "%(senderName)s a rendu l'historique visible à tous les membres du salon.", - "%(senderName)s made future room history visible to anyone.": "%(senderName)s a rendu l'historique visible à n'importe qui.", - "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s a rendu l'historique visible à inconnu (%(visibility)s).", + "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s a rendu l’historique visible à tous les membres du salon, depuis le moment où ils ont été invités.", + "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s a rendu l’historique visible à tous les membres du salon, à partir de leur arrivée.", + "%(senderName)s made future room history visible to all room members.": "%(senderName)s a rendu l’historique visible à tous les membres du salon.", + "%(senderName)s made future room history visible to anyone.": "%(senderName)s a rendu l’historique visible à tout le monde.", + "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s a rendu l’historique visible à inconnu (%(visibility)s).", "Manage Integrations": "Gestion des intégrations", "Missing room_id in request": "Absence du room_id dans la requête", "Missing user_id in request": "Absence du user_id dans la requête", @@ -120,7 +120,7 @@ "New passwords don't match": "Les mots de passe ne correspondent pas", "New passwords must match each other.": "Les nouveaux mots de passe doivent être identiques.", "not specified": "non spécifié", - "(not supported by this browser)": "(non supporté par ce navigateur)", + "(not supported by this browser)": "(non pris en charge par ce navigateur)", "": "", "No more results": "Fin des résultats", "No results": "Pas de résultat", @@ -141,7 +141,7 @@ "No users have specific privileges in this room": "Aucun utilisateur n’a de privilège spécifique dans ce salon", "olm version:": "version de olm :", "Please check your email and click on the link it contains. Once this is done, click continue.": "Veuillez vérifier vos e-mails et cliquer sur le lien que vous avez reçu. Puis cliquez sur continuer.", - "Power level must be positive integer.": "Le niveau d'autorité doit être un entier positif.", + "Power level must be positive integer.": "Le rang doit être un entier positif.", "Privileged Users": "Utilisateurs privilégiés", "Profile": "Profil", "Reason": "Raison", @@ -151,10 +151,10 @@ "%(senderName)s removed their profile picture.": "%(senderName)s a supprimé son image de profil.", "%(senderName)s requested a VoIP conference.": "%(senderName)s a demandé une téléconférence audio.", "Return to login screen": "Retourner à l’écran de connexion", - "%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s n’a pas la permission de vous envoyer des notifications - merci de vérifier les paramètres de votre navigateur", - "%(brand)s was not given permission to send notifications - please try again": "%(brand)s n’a pas reçu la permission de vous envoyer des notifications - veuillez réessayer", + "%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s n’a pas l’autorisation de vous envoyer des notifications - merci de vérifier les paramètres de votre navigateur", + "%(brand)s was not given permission to send notifications - please try again": "%(brand)s n’a pas reçu l’autorisation de vous envoyer des notifications - veuillez réessayer", "%(brand)s version:": "Version de %(brand)s :", - "Room %(roomId)s not visible": "Le salon %(roomId)s n'est pas visible", + "Room %(roomId)s not visible": "Le salon %(roomId)s n’est pas visible", "Room Colour": "Couleur du salon", "Rooms": "Salons", "Search": "Rechercher", @@ -165,7 +165,7 @@ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s a invité %(targetDisplayName)s à rejoindre le salon.", "Server error": "Erreur du serveur", "Server may be unavailable, overloaded, or search timed out :(": "Le serveur semble être inaccessible, surchargé ou la recherche a expiré :(", - "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être indisponible, surchargé ou vous avez rencontré un problème.", + "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être indisponible, surchargé ou vous êtes tombé sur un bug.", "Server unavailable, overloaded, or something else went wrong.": "Le serveur semble être inaccessible, surchargé ou quelque chose s'est mal passé.", "Session ID": "Identifiant de session", "%(senderName)s set a profile picture.": "%(senderName)s a défini une image de profil.", @@ -175,7 +175,7 @@ "Sign in": "Se connecter", "Sign out": "Se déconnecter", "%(count)s of your messages have not been sent.|other": "Certains de vos messages n’ont pas été envoyés.", - "Someone": "Quelqu'un", + "Someone": "Quelqu’un", "Submit": "Soumettre", "Success": "Succès", "This email address is already in use": "Cette adresse e-mail est déjà utilisée", @@ -183,7 +183,7 @@ "The email address linked to your account must be entered.": "L’adresse e-mail liée à votre compte doit être renseignée.", "The remote side failed to pick up": "Le correspondant n’a pas décroché", "This room has no local addresses": "Ce salon n'a pas d'adresse locale", - "This room is not recognised.": "Ce salon n'est pas reconnu.", + "This room is not recognised.": "Ce salon n’est pas reconnu.", "This doesn't appear to be a valid email address": "Cette adresse e-mail ne semble pas valide", "This phone number is already in use": "Ce numéro de téléphone est déjà utilisé", "This room is not accessible by remote Matrix servers": "Ce salon n’est pas accessible par les serveurs Matrix distants", @@ -195,8 +195,8 @@ "Unable to verify email address.": "Impossible de vérifier l’adresse e-mail.", "Unban": "Révoquer le bannissement", "%(senderName)s unbanned %(targetName)s.": "%(senderName)s a révoqué le bannissement de %(targetName)s.", - "Unable to capture screen": "Impossible de faire une capture d'écran", - "Unable to enable Notifications": "Impossible d'activer les notifications", + "Unable to capture screen": "Impossible de faire une capture d’écran", + "Unable to enable Notifications": "Impossible d’activer les notifications", "Unmute": "Activer le son", "Upload avatar": "Télécharger une photo de profil", "Upload Failed": "Échec de l’envoi", @@ -208,19 +208,19 @@ "Voice call": "Appel vocal", "VoIP conference finished.": "Téléconférence VoIP terminée.", "VoIP conference started.": "Téléconférence VoIP démarrée.", - "VoIP is unsupported": "Voix sur IP non gérée", + "VoIP is unsupported": "Voix sur IP non prise en charge", "Warning!": "Attention !", "Who can access this room?": "Qui peut accéder au salon ?", "Who can read history?": "Qui peut lire l'historique ?", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s a annulé l’invitation de %(targetName)s.", "You are already in a call.": "Vous avez déjà un appel en cours.", - "You cannot place a call with yourself.": "Vous ne pouvez pas passer d'appel avec vous-même.", - "You cannot place VoIP calls in this browser.": "Vous ne pouvez pas passer d'appel en Voix sur IP dans ce navigateur.", + "You cannot place a call with yourself.": "Vous ne pouvez pas passer d’appel avec vous-même.", + "You cannot place VoIP calls in this browser.": "Vous ne pouvez pas passer d’appel en Voix sur IP dans ce navigateur.", "You do not have permission to post to this room": "Vous n’avez pas la permission de poster dans ce salon", "You have no visible notifications": "Vous n'avez pas de notification visible", - "You need to be able to invite users to do that.": "Vous devez être capable d’inviter des utilisateurs pour faire ça.", + "You need to be able to invite users to do that.": "Vous devez avoir l’autorisation d’inviter des utilisateurs pour faire ceci.", "You need to be logged in.": "Vous devez être identifié.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Votre adresse e-mail ne semble pas être associée à un identifiant Matrix sur ce serveur d'accueil.", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Votre adresse e-mail ne semble pas être associée à un identifiant Matrix sur ce serveur d’accueil.", "You seem to be in a call, are you sure you want to quit?": "Vous semblez avoir un appel en cours, voulez-vous vraiment partir ?", "You seem to be uploading files, are you sure you want to quit?": "Vous semblez être en train d'envoyer des fichiers, voulez-vous vraiment partir ?", "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Vous ne pourrez pas annuler cette modification car vous promouvez l’utilisateur au même rang que le vôtre.", @@ -377,7 +377,7 @@ "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (rang %(powerLevelNumber)s)", "(could not connect media)": "(impossible de se connecter au média)", "(no answer)": "(pas de réponse)", - "(unknown failure: %(reason)s)": "(erreur inconnue : %(reason)s)", + "(unknown failure: %(reason)s)": "(erreur inconnue : %(reason)s)", "Your browser does not support the required cryptography extensions": "Votre navigateur ne supporte pas les extensions cryptographiques nécessaires", "Not a valid %(brand)s keyfile": "Fichier de clé %(brand)s non valide", "Authentication check failed: incorrect password?": "Erreur d’authentification : mot de passe incorrect ?", @@ -388,13 +388,13 @@ "Add a widget": "Ajouter un widget", "Allow": "Autoriser", "Delete widget": "Supprimer le widget", - "Define the power level of a user": "Définir le rang d'un utilisateur", + "Define the power level of a user": "Définir le rang d’un utilisateur", "Edit": "Modifier", "Enable automatic language detection for syntax highlighting": "Activer la détection automatique de la langue pour la correction orthographique", "To get started, please pick a username!": "Pour commencer, choisissez un nom d'utilisateur !", "Unable to create widget.": "Impossible de créer le widget.", - "You are not in this room.": "Vous n'êtes pas dans ce salon.", - "You do not have permission to do that in this room.": "Vous n'avez pas la permission d'effectuer cette action dans ce salon.", + "You are not in this room.": "Vous n’êtes pas dans ce salon.", + "You do not have permission to do that in this room.": "Vous n’avez pas l’autorisation d’effectuer cette action dans ce salon.", "Example": "Exemple", "Create": "Créer", "Featured Rooms:": "Salons mis en avant :", @@ -411,21 +411,21 @@ "Copied!": "Copié !", "Failed to copy": "Échec de la copie", "%(widgetName)s widget modified by %(senderName)s": "Widget %(widgetName)s modifié par %(senderName)s", - "Who would you like to add to this community?": "Qui souhaitez-vous ajouter à cette communauté ?", - "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Attention : toute personne ajoutée à une communauté sera visible par tous ceux connaissant l'identifiant de la communauté", - "Invite new community members": "Inviter de nouveaux membres dans cette communauté", - "Which rooms would you like to add to this community?": "Quels salons souhaitez-vous ajouter à cette communauté ?", + "Who would you like to add to this community?": "Qui souhaitez-vous ajouter à cette communauté ?", + "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Attention : toute personne ajoutée à une communauté sera visible par tous ceux connaissant l’identifiant de la communauté", + "Invite new community members": "Inviter de nouvelles personnes dans cette communauté", + "Which rooms would you like to add to this community?": "Quels salons souhaitez-vous ajouter à cette communauté ?", "Add rooms to the community": "Ajouter des salons à la communauté", "Add to community": "Ajouter à la communauté", - "Failed to invite the following users to %(groupId)s:": "Échec de l'invitation des utilisateurs à %(groupId)s :", - "Failed to invite users to community": "Échec de l'invitation d'utilisateurs à la communauté", - "Failed to invite users to %(groupId)s": "Échec de l'invitation d'utilisateurs à %(groupId)s", - "Failed to add the following rooms to %(groupId)s:": "Échec de l'ajout des salons suivants à %(groupId)s :", + "Failed to invite the following users to %(groupId)s:": "Échec de l’invitation des utilisateurs suivants à %(groupId)s :", + "Failed to invite users to community": "Échec de l’invitation des utilisateurs à la communauté", + "Failed to invite users to %(groupId)s": "Échec de l’invitation des utilisateurs à %(groupId)s", + "Failed to add the following rooms to %(groupId)s:": "Échec de l’ajout des salons suivants à %(groupId)s :", "Ignored user": "Utilisateur ignoré", - "You are now ignoring %(userId)s": "Dorénavant vous ignorez %(userId)s", - "Unignored user": "Utilisateur n'étant plus ignoré", - "You are no longer ignoring %(userId)s": "Vous n'ignorez plus %(userId)s", - "Invite to Community": "Inviter dans la Communauté", + "You are now ignoring %(userId)s": "Vous ignorez désormais %(userId)s", + "Unignored user": "L’utilisateur n’est plus ignoré", + "You are no longer ignoring %(userId)s": "Vous n’ignorez plus %(userId)s", + "Invite to Community": "Inviter dans la communauté", "Communities": "Communautés", "Message Pinning": "Épingler un message", "Mention": "Mentionner", @@ -573,7 +573,7 @@ "Mirror local video feed": "Inverser horizontalement la vidéo locale (effet miroir)", "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Un e-mail a été envoyé à %(emailAddress)s. Après avoir suivi le lien présent dans celui-ci, cliquez ci-dessous.", "Ignores a user, hiding their messages from you": "Ignore un utilisateur, en masquant ses messages", - "Stops ignoring a user, showing their messages going forward": "N'ignore plus un utilisateur, en affichant ses messages à partir de maintenant", + "Stops ignoring a user, showing their messages going forward": "Arrête d’ignorer un utilisateur, en affichant ses messages à partir de maintenant", "The visibility of '%(roomName)s' in %(groupId)s could not be updated.": "La visibilité de \"%(roomName)s\" dans %(groupId)s n'a pas pu être mise à jour.", "Visibility in Room List": "Visibilité dans la liste des salons", "Visible to everyone": "Visible pour tout le monde", @@ -609,7 +609,7 @@ "Display your community flair in rooms configured to show it.": "Sélectionnez les badges dans les paramètres de chaque salon pour les afficher.", "expand": "développer", "collapse": "réduire", - "Call Failed": "L'appel a échoué", + "Call Failed": "L’appel a échoué", "Send": "Envoyer", "Old cryptography data detected": "Anciennes données de chiffrement détectées", "Data from an older version of %(brand)s has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Nous avons détecté des données d'une ancienne version de %(brand)s. Le chiffrement de bout en bout n'aura pas fonctionné correctement sur l'ancienne version. Les messages chiffrés échangés récemment dans l'ancienne version ne sont peut-être pas déchiffrables dans cette version. Les échanges de message avec cette version peuvent aussi échouer. Si vous rencontrez des problèmes, déconnectez-vous puis reconnectez-vous. Pour conserver l'historique des messages, exportez puis réimportez vos clés de chiffrement.", @@ -625,13 +625,13 @@ "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Le respect de votre vie privée est important pour nous, donc nous ne collectons aucune donnée personnelle ou permettant de vous identifier pour nos statistiques.", "Learn more about how we use analytics.": "En savoir plus sur notre utilisation des statistiques.", "The information being sent to us to help make %(brand)s better includes:": "Les informations qui nous sont envoyées et qui nous aident à améliorer %(brand)s comportent :", - "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Si la page contient des informations permettant de vous identifier, comme un salon, un identifiant d'utilisateur ou de groupe, ces données sont enlevées avant qu'elle ne soit envoyée au serveur.", + "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Si la page contient des informations permettant de vous identifier, comme un salon, un identifiant d’utilisateur ou de groupe, ces données sont enlevées avant qu’elle ne soit envoyée au serveur.", "The platform you're on": "La plateforme que vous utilisez", "The version of %(brand)s": "La version de %(brand)s", "Your language of choice": "La langue que vous avez choisie", - "Which officially provided instance you are using, if any": "L'instance officielle que vous utilisez, si vous en utilisez une", - "Whether or not you're using the Richtext mode of the Rich Text Editor": "Si vous utilisez le mode « texte enrichi » de l'éditeur de texte enrichi", - "Your homeserver's URL": "L'URL de votre serveur d'accueil", + "Which officially provided instance you are using, if any": "L’instance officielle que vous utilisez, si vous en utilisez une", + "Whether or not you're using the Richtext mode of the Rich Text Editor": "Si vous utilisez le mode « texte enrichi » de l’éditeur de texte enrichi", + "Your homeserver's URL": "L’URL de votre serveur d’accueil", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s %(day)s %(monthName)s %(fullYear)s", "This room is not public. You will not be able to rejoin without an invite.": "Ce salon n'est pas public. Vous ne pourrez pas y revenir sans invitation.", "Community IDs cannot be empty.": "Les identifiants de communauté ne peuvent pas être vides.", @@ -648,7 +648,7 @@ "Code": "Code", "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Si vous avez signalé un bug via GitHub, les journaux de débogage peuvent nous aider à identifier le problème. Les journaux de débogage contiennent des données d'utilisation de l'application dont votre nom d'utilisateur, les identifiants ou alias des salons ou groupes que vous avez visité et les noms d'utilisateur des autres participants. Ils ne contiennent pas les messages.", "Submit debug logs": "Envoyer les journaux de débogage", - "Opens the Developer Tools dialog": "Ouvre la fenêtre des Outils de développeur", + "Opens the Developer Tools dialog": "Ouvre la fenêtre des outils de développeur", "Unable to join community": "Impossible de rejoindre la communauté", "Unable to leave community": "Impossible de quitter la communauté", "Changes made to your community name and avatar might not be seen by other users for up to 30 minutes.": "Les changements effectués au nom et à l'avatar de votre communauté peuvent prendre jusqu'à 30 minutes avant d'être vus par d'autres utilisateurs.", @@ -784,7 +784,7 @@ "Preparing to send logs": "Préparation d'envoi des journaux", "Missing roomId.": "Identifiant de salon manquant.", "Popout widget": "Détacher le widget", - "Every page you use in the app": "Toutes les pages que vous utilisez dans l'application", + "Every page you use in the app": "Toutes les pages que vous utilisez dans l’application", "e.g. ": "par ex. ", "Your device resolution": "La résolution de votre appareil", "Always show encryption icons": "Toujours afficher les icônes de chiffrement", @@ -832,8 +832,8 @@ "Demote yourself?": "Vous rétrograder ?", "Demote": "Rétrograder", "This event could not be displayed": "Cet événement n'a pas pu être affiché", - "Permission Required": "Permission requise", - "You do not have permission to start a conference call in this room": "Vous n'avez pas la permission de lancer un appel en téléconférence dans ce salon", + "Permission Required": "Autorisation requise", + "You do not have permission to start a conference call in this room": "Vous n’avez pas l’autorisation de lancer un appel en téléconférence dans ce salon", "A call is currently being placed!": "Un appel est en cours !", "Failed to remove widget": "Échec de la suppression du widget", "An error ocurred whilst trying to remove the widget from the room": "Une erreur est survenue lors de la suppression du widget du salon", @@ -862,8 +862,8 @@ "Upgrade this room to version %(version)s": "Mettre à niveau ce salon vers la version %(version)s", "Forces the current outbound group session in an encrypted room to be discarded": "Force la session de groupe sortante actuelle dans un salon chiffré à être rejetée", "Unable to connect to Homeserver. Retrying...": "Impossible de se connecter au serveur d'accueil. Reconnexion...", - "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s a défini l'adresse principale pour ce salon comme %(address)s.", - "%(senderName)s removed the main address for this room.": "%(senderName)s a supprimé l'adresse principale de ce salon.", + "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s a défini l’adresse principale pour ce salon comme %(address)s.", + "%(senderName)s removed the main address for this room.": "%(senderName)s a supprimé l’adresse principale de ce salon.", "%(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 utilise maintenant 3 à 5 fois moins de mémoire, en ne chargeant les informations des autres utilisateurs que quand elles sont nécessaires. Veuillez patienter pendant que l'on se resynchronise avec le serveur !", "Updating %(brand)s": "Mise à jour de %(brand)s", "Before submitting logs, you must create a GitHub issue to describe your problem.": "Avant de soumettre vos journaux, vous devez créer une « issue » sur GitHub pour décrire votre problème.", @@ -934,7 +934,7 @@ "Common names and surnames are easy to guess": "Les noms et prénoms répandus sont faciles à deviner", "Use a longer keyboard pattern with more turns": "Utilisez un schéma plus long et avec plus de variations", "Failed to load group members": "Échec du chargement des membres du groupe", - "Failed to invite users to the room:": "Échec de l'invitation d'utilisateurs dans le salon :", + "Failed to invite users to the room:": "Échec de l’invitation d'utilisateurs dans le salon :", "There was an error joining the room": "Une erreur est survenue en rejoignant le salon", "You do not have permission to invite people to this room.": "Vous n'avez pas la permission d'envoyer des invitations dans ce salon.", "User %(user_id)s does not exist": "L'utilisateur %(user_id)s n'existe pas", @@ -968,14 +968,14 @@ "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Impossible de trouver les profils pour les identifiants Matrix listés ci-dessous. Voulez-vous quand même les inviter ?", "Invite anyway and never warn me again": "Inviter quand même et ne plus me prévenir", "Invite anyway": "Inviter quand même", - "Whether or not you're logged in (we don't record your username)": "Si vous êtes connecté ou pas (votre nom d'utilisateur n'est pas enregistré)", + "Whether or not you're logged in (we don't record your username)": "Si vous êtes connecté ou pas (votre nom d'utilisateur n’est pas enregistré)", "Upgrades a room to a new version": "Met à niveau un salon vers une nouvelle version", "Sets the room name": "Définit le nom du salon", "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s a mis à niveau ce salon.", "%(displayName)s is typing …": "%(displayName)s est en train d'écrire…", - "%(names)s and %(count)s others are typing …|other": "%(names)s et %(count)s autres sont en train d'écrire…", - "%(names)s and %(count)s others are typing …|one": "%(names)s et un autre sont en train d'écrire…", - "%(names)s and %(lastPerson)s are typing …": "%(names)s et %(lastPerson)s sont en train d'écrire…", + "%(names)s and %(count)s others are typing …|other": "%(names)s et %(count)s autres sont en train d’écrire…", + "%(names)s and %(count)s others are typing …|one": "%(names)s et un autre sont en train d’écrire…", + "%(names)s and %(lastPerson)s are typing …": "%(names)s et %(lastPerson)s sont en train d’écrire…", "Enable Emoji suggestions while typing": "Activer la suggestion d’émojis lors de la saisie", "Render simple counters in room header": "Afficher des compteurs simples dans l’en-tête des salons", "Show a placeholder for removed messages": "Afficher les messages supprimés", @@ -1084,7 +1084,7 @@ "A new recovery passphrase and key for Secure Messages have been detected.": "Un nouveau mot de passe et une nouvelle clé de récupération pour les messages sécurisés ont été détectés.", "Recovery Method Removed": "Méthode de récupération supprimée", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Si vous n'avez pas supprimé la méthode de récupération, un attaquant peut être en train d'essayer d'accéder à votre compte. Modifiez le mot de passe de votre compte et configurez une nouvelle méthode de récupération dans les réglages.", - "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Le fichier \"%(fileName)s\" dépasse la taille limite autorisée par ce serveur pour les téléchargements", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Le fichier « %(fileName)s » dépasse la taille limite autorisée par ce serveur pour les envois", "Gets or sets the room topic": "Récupère ou définit le sujet du salon", "This room has no topic.": "Ce salon n'a pas de sujet.", "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s a rendu le salon public à tous ceux qui en connaissent le lien.", @@ -1092,7 +1092,7 @@ "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s a changé la règle d’adhésion en %(rule)s", "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s a autorisé les visiteurs à rejoindre le salon.", "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s a empêché les visiteurs de rejoindre le salon.", - "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a changé l'accès des visiteurs en %(rule)s", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s a changé l’accès des visiteurs en %(rule)s", "Group & filter rooms by custom tags (refresh to apply changes)": "Grouper et filtrer les salons grâce à des étiquettes personnalisées (actualiser pour appliquer les changements)", "Verify this user by confirming the following emoji appear on their screen.": "Vérifier cet utilisateur en confirmant que les émojis suivant apparaissent sur son écran.", "Unable to find a supported verification method.": "Impossible de trouver une méthode de vérification prise en charge.", @@ -1206,7 +1206,7 @@ "Create your Matrix account on %(serverName)s": "Créez votre compte Matrix sur %(serverName)s", "Could not load user profile": "Impossible de charger le profil de l’utilisateur", "Your Matrix account on %(serverName)s": "Votre compte Matrix sur %(serverName)s", - "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Ajoute ¯\\_(ツ)_/¯ devant un message en texte brut", + "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Ajoute ¯\\_(ツ)_/¯ en préfixe du message", "User %(userId)s is already in the room": "L’utilisateur %(userId)s est déjà membre du salon", "The user must be unbanned before they can be invited.": "Le bannissement de l’utilisateur doit être révoqué avant de pouvoir l’inviter.", "Upgrade to your own domain": "Mettre à niveau vers votre propre domaine", @@ -1452,7 +1452,7 @@ "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Si vous ne voulez pas utiliser pour découvrir et être découvrable par les contacts que vous connaissez, saisissez un autre serveur d’identité ci-dessous.", "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.": "L’utilisation d’un serveur d’identité est optionnelle. Si vous ne choisissez pas d’utiliser un serveur d’identité, les autres utilisateurs ne pourront pas vous découvrir et vous ne pourrez pas en inviter par e-mail ou par téléphone.", "Do not use an identity server": "Ne pas utiliser de serveur d’identité", - "You do not have the required permissions to use this command.": "Vous n’avez pas les permissions nécessaires pour utiliser cette commande.", + "You do not have the required permissions to use this command.": "Vous n’avez pas les autorisations nécessaires pour utiliser cette commande.", "Upgrade the room": "Mettre à niveau le salon", "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Renseignez un e-mail pour la récupération de compte. Utilisez un e-mail ou un téléphone pour être éventuellement découvrable par des contacts existants.", "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Renseignez un e-mail pour la récupération de compte. Utilisez un e-mail pour être éventuellement découvrable par des contacts existants.", @@ -1580,7 +1580,7 @@ "Unread messages.": "Messages non lus.", "Show tray icon and minimize window to it on close": "Afficher l’icône dans la barre d’état et minimiser la fenêtre lors de la fermeture", "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.": "Cette action nécessite l’accès au serveur d’identité par défaut afin de valider une adresse e-mail ou un numéro de téléphone, mais le serveur n’a aucune condition de service.", - "Trust": "Confiance", + "Trust": "Faire confiance", "Message Actions": "Actions de message", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", "You verified %(name)s": "Vous avez vérifié %(name)s", @@ -1665,9 +1665,9 @@ "Verification Request": "Demande de vérification", "Match system theme": "S’adapter au thème du système", "%(senderName)s placed a voice call.": "%(senderName)s a passé un appel vocal.", - "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s a passé un appel vocal. (pas pris en charge par ce navigateur)", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s a passé un appel vocal. (non pris en charge par ce navigateur)", "%(senderName)s placed a video call.": "%(senderName)s a passé un appel vidéo.", - "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s a passé un appel vidéo. (pas pris en charge par ce navigateur)", + "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s a passé un appel vidéo. (non pris en charge par ce navigateur)", "Clear notifications": "Vider les notifications", "Customise your experience with experimental labs features. Learn more.": "Personnalisez votre expérience avec des fonctionnalités expérimentales du labo. En savoir plus.", "Error upgrading room": "Erreur lors de la mise à niveau du salon", @@ -1838,7 +1838,7 @@ "Unknown (user, session) pair:": "Paire (utilisateur, session) inconnue :", "Session already verified!": "Session déjà vérifiée !", "WARNING: Session already verified, but keys do NOT MATCH!": "ATTENTION : La session a déjà été vérifiée mais les clés NE CORRESPONDENT PAS !", - "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!": "ATTENTION : ÉCHEC DE LA VÉRIFICATION DE CLÉ ! La clé de signature pour %(userId)s et la session %(deviceId)s est « %(fprint)s » que ne correspond pas à la clé fournie « %(fingerprint)s ». Cela pourrait signifier que vos communications sont interceptées !", + "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!": "ATTENTION : ÉCHEC DE LA VÉRIFICATION DE CLÉ ! La clé de signature pour %(userId)s et la session %(deviceId)s est « %(fprint)s  ce qui ne correspond pas à la clé fournie « %(fingerprint)s ». Cela pourrait signifier que vos communications sont interceptées !", "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "La clé de signature que vous avez fournie correspond à celle que vous avez reçue de la session %(deviceId)s de %(userId)s. Session marquée comme vérifiée.", "Never send encrypted messages to unverified sessions from this session": "Ne jamais envoyer de messages chiffrés aux sessions non vérifiées depuis cette session", "Never send encrypted messages to unverified sessions in this room from this session": "Ne jamais envoyer des messages chiffrés aux sessions non vérifiées dans ce salon depuis cette session", @@ -1940,7 +1940,7 @@ "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) s’est connecté à une nouvelle session sans la vérifier :", "Ask this user to verify their session, or manually verify it below.": "Demandez à cet utilisateur de vérifier sa session, ou vérifiez-la manuellement ci-dessous.", "Verify by scanning": "Vérifier en scannant", - "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Si vous utilisez %(brand)s sur un appareil où le toucher est le mécanisme primaire de saisie", + "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Si vous utilisez %(brand)s sur un appareil où le tactile est le mode principal de saisie", "Whether you're using %(brand)s as an installed Progressive Web App": "Si vous utilisez %(brand)s en tant qu’application web progressive (PWA)", "Your user agent": "Votre agent utilisateur", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "La session que vous essayez de vérifier ne prend pas en charge les codes QR ou la vérification d’émojis, qui sont les méthodes prises en charge par %(brand)s. Essayez avec un autre client.", @@ -1960,7 +1960,7 @@ "The internet connection either session is using": "La connection internet de l'une des sessions", "We recommend you change your password and recovery key in Settings immediately": "Nous vous recommandons de changer votre mot de passe et la clé de récupération dans Paramètres dès que possible", "Sign In or Create Account": "Se connecter ou créer un compte", - "Use your account or create a new one to continue.": "Utilisez votre compte ou créez un nouveau compte pour continuer.", + "Use your account or create a new one to continue.": "Utilisez votre compte ou créez en un pour continuer.", "Create Account": "Créer un compte", "Order rooms by name": "Trier les salons par nom", "Show rooms with unread notifications first": "Afficher en premier les salons avec des notifications non lues", @@ -2378,7 +2378,7 @@ "%(brand)s Desktop": "%(brand)s Desktop", "%(brand)s iOS": "%(brand)s iOS", "%(brand)s X for Android": "%(brand)s X pour Android", - "Are you sure you want to cancel entering passphrase?": "Souhaitez-vous vraiment annuler l'entrée de la phrase de passe ?", + "Are you sure you want to cancel entering passphrase?": "Souhaitez-vous vraiment annuler la saisie de la phrase de passe ?", "Unexpected server error trying to leave the room": "Erreur de serveur inattendue en essayant de quitter le salon", "Error leaving room": "Erreur en essayant de quitter le salon", "The person who invited you already left the room.": "La personne vous ayant invité a déjà quitté le salon.", @@ -2400,18 +2400,18 @@ "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.", + "%(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", + "%(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 ( ͡° ͜ʖ ͡°) en préfixe du message", "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.", + "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.", + "The call could not be established": "L’appel n’a pas pu être établi", + "The other party declined the call.": "Le correspondant a décliné l’appel.", "Call Declined": "Appel rejeté", "Ignored attempt to disable encryption": "Essai de désactiver le chiffrement ignoré", "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.": "Ajoutez les utilisateurs et les serveurs que vous voulez ignorer ici. Utilisez des astérisques pour que %(brand)s comprenne tous les caractères. Par exemple, @bot:* va ignorer tous les utilisateurs ayant le nom 'bot' sur n'importe quel serveur.", @@ -2428,8 +2428,8 @@ "Offline encrypted messaging using dehydrated devices": "Messagerie hors-ligne chiffrée utilisant des appareils déshydratés", "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Prototype de communautés v2. Requiert un serveur d'accueil compatible. Très expérimental - à utiliser avec précaution.", "Safeguard against losing access to encrypted messages & data": "Sécurité contre la perte d'accès aux messages & données chiffrées", - "(their device couldn't start the camera / microphone)": "(son appareil ne peut pas démarrer la caméra / le microphone)", - "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Tous les serveurs sont interdits de participation ! Ce salon ne peut plus être utilisé.", + "(their device couldn't start the camera / microphone)": "(leur appareil ne peut pas démarrer la caméra/le microphone)", + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Tous les serveurs ont été bannis ! Ce salon ne peut plus être utilisé.", "What's the name of your community or team?": "Quel est le nom de votre communauté ou équipe ?", "You can change this later if needed.": "Vous pouvez modifier ceci après si besoin.", "Use this when referencing your community to others. The community ID cannot be changed.": "Utilisez ceci lorsque vous faites référence à votre communauté aux autres. L'identifiant de la communauté ne peut pas être modifié.", @@ -2570,12 +2570,12 @@ "Afghanistan": "Afghanistan", "United States": "États-Unis", "United Kingdom": "Royaume-Uni", - "You've reached the maximum number of simultaneous calls.": "Vous avez atteint le nombre maximum d'appels en simultané.", - "No other application is using the webcam": "Aucune autre application n'est en train d'utiliser la caméra", - "A microphone and webcam are plugged in and set up correctly": "Un microphone et une caméra sont branchées et bien configurées", - "Unable to access webcam / microphone": "Impossible d'accéder à la caméra ou microphone", - "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "La fonction a échoué car le microphone n'a pas pu être accédé. Vérifiez qu'un microphone est branché et bien configuré.", - "Unable to access microphone": "Impossible d'accéder au microphone", + "You've reached the maximum number of simultaneous calls.": "Vous avez atteint le nombre maximum d’appels en simultané.", + "No other application is using the webcam": "Aucune autre application n’est en train d’utiliser la caméra", + "A microphone and webcam are plugged in and set up correctly": "Un microphone et une caméra sont branchées et bien configurés", + "Unable to access webcam / microphone": "Impossible d’accéder à la caméra ou au microphone", + "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "La fonction a échoué faute de pouvoir accéder au microphone. Vérifiez qu’un microphone est branché et bien configuré.", + "Unable to access microphone": "Impossible d’accéder au microphone", "Belgium": "Belgique", "Belarus": "Biélorussie", "Barbados": "Barbade", @@ -2591,15 +2591,15 @@ "Antigua & Barbuda": "Antigue-et-Barbude", "Antarctica": "Antarctique", "Anguilla": "Anguilla", - "Angola": "République d'Angola", + "Angola": "République d’Angola", "Andorra": "Andorre", "American Samoa": "Samoa américaines", "Invite someone using their name, email address, username (like ) or share this room.": "Invitez quelqu'un via leur nom, e-mail ou nom d'utilisateur (p. ex. ) ou partagez ce salon.", "Start a conversation with someone using their name, email address or username (like ).": "Commencer une conversation avec quelqu'un via leur nom, e-mail ou nom d'utilisateur (comme par exemple ).", - "Too Many Calls": "Trop d'appels", - "Permission is granted to use the webcam": "Permission accordée pour l'utilisation de la webcam", - "Call failed because webcam or microphone could not be accessed. Check that:": "La fonction a échoué car la webcam ou le microphone ne pouvait pas être accédé. Vérifiez que :", - "Send stickers to this room as you": "Envoyer des stickers dans ce salon en tant que vous", + "Too Many Calls": "Trop d’appels", + "Permission is granted to use the webcam": "L’autorisation d’accéder à la caméra a été accordée", + "Call failed because webcam or microphone could not be accessed. Check that:": "La fonction a échoué faute de pouvoir accéder à la caméra ou au microphone. Vérifiez que :", + "Send stickers to this room as you": "Envoyer des stickers dans ce salon en tant que vous-même", "Zambia": "Zambie", "Yemen": "Yémen", "Western Sahara": "Sahara occidental", @@ -2685,7 +2685,7 @@ "North Korea": "Corée du Nord", "Norfolk Island": "Île Norfolk", "Niue": "Niue", - "Nigeria": "Nigéria", + "Nigeria": "Nigeria", "Niger": "Niger", "Nicaragua": "Nicaragua", "New Zealand": "Nouvelle-Zélande", @@ -2703,7 +2703,7 @@ "Monaco": "Monaco", "Moldova": "Moldavie", "Micronesia": "États fédérés de Micronésie", - "Mexico": "Mexico", + "Mexico": "Mexique", "Mayotte": "Mayotte", "Mauritius": "République de Maurice", "Mauritania": "Mauritanie", @@ -2783,13 +2783,13 @@ "Equatorial Guinea": "Guinée équatoriale", "El Salvador": "Le Salvador", "Egypt": "Égypte", - "Ecuador": "République de l'Équateur", + "Ecuador": "République de l’Équateur", "Dominican Republic": "République dominicaine", "Dominica": "Dominique", "Djibouti": "Djibouti", "Denmark": "Danemark", - "Côte d’Ivoire": "Côte d’Ivoire (Terre d'Éburnie)", - "Czech Republic": "La République tchèque", + "Côte d’Ivoire": "Côte d’Ivoire", + "Czech Republic": "République tchèque", "Cyprus": "Chypre", "Curaçao": "Curaçao", "Cuba": "Cuba", @@ -2799,7 +2799,7 @@ "Congo - Kinshasa": "République démocratique du Congo", "Congo - Brazzaville": "République du Congo", "Comoros": "Comores", - "Colombia": "Colombia", + "Colombia": "Colombie", "Cocos (Keeling) Islands": "îles Cocos", "Christmas Island": "île Christmas", "China": "Chine", @@ -2812,12 +2812,12 @@ "Canada": "Canada", "Cameroon": "Cameroun", "Cambodia": "Cambodge", - "Burundi": "La république du Burundi", + "Burundi": "République du Burundi", "Burkina Faso": "Burkina Faso", "Bulgaria": "Bulgarie", "Brunei": "Brunéi", "British Virgin Islands": "Îles Vierges britanniques", - "British Indian Ocean Territory": "Territoire britannique de l'océan Indien", + "British Indian Ocean Territory": "Territoire britannique de l’océan Indien", "Brazil": "Brésil", "Bouvet Island": "Île Bouvet", "Botswana": "Botswana", @@ -2825,14 +2825,14 @@ "Bolivia": "Bolivie", "Bhutan": "Bhoutan", "Bermuda": "Bermudes", - "with state key %(stateKey)s": "avec la ou les clés d'état %(stateKey)s", - "with an empty state key": "avec une clé d'état vide", - "See when anyone posts a sticker to your active room": "Voir quand n'importe qui envoye un sticker dans le salon actuel", + "with state key %(stateKey)s": "avec la ou les clés d’état %(stateKey)s", + "with an empty state key": "avec une clé d’état vide", + "See when anyone posts a sticker to your active room": "Voir quand n’importe qui envoie un sticker dans le salon actuel", "See when a sticker is posted in this room": "Voir quand un sticker est envoyé dans ce salon", - "See when the avatar changes in your active room": "Voir quand l'avatar change dans le salon actuel", - "Change the avatar of your active room": "Changer l'avatar du salon actuel", - "See when the avatar changes in this room": "Voir quand l'avatar change dans ce salon", - "Change the avatar of this room": "Changer l'avatar de ce salon", + "See when the avatar changes in your active room": "Voir quand l’avatar change dans le salon actuel", + "Change the avatar of your active room": "Changer l’avatar du salon actuel", + "See when the avatar changes in this room": "Voir quand l’avatar change dans ce salon", + "Change the avatar of this room": "Changer l’avatar de ce salon", "Send stickers into your active room": "Envoyer des stickers dans le salon actuel", "See when the topic changes in this room": "Voir quand le sujet change dans ce salon", "See when the topic changes in your active room": "Voir quand le sujet change dans le salon actuel", @@ -2842,35 +2842,35 @@ "Change the topic of your active room": "Changer le sujet dans le salon actuel", "Change the topic of this room": "Changer le sujet de ce salon", "Send stickers into this room": "Envoyer des stickers dans ce salon", - "Remain on your screen when viewing another room, when running": "Reste sur votre écran quand vous regardez un autre salon lors de l'appel", - "Takes the call in the current room off hold": "Reprends l'appel en cours dans ce salon", - "Places the call in the current room on hold": "Met l'appel en pause dans ce salon", - "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Ajoute \"(╯°□°)╯︵ ┻━┻\" en préfixe du message", - "Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message": "Ajoute \"┬──┬ ノ( ゜-゜ノ)\" en préfixe du message", + "Remain on your screen when viewing another room, when running": "Reste sur votre écran quand vous regardez un autre salon lors de l’appel", + "Takes the call in the current room off hold": "Reprend l’appel en cours dans ce salon", + "Places the call in the current room on hold": "Met l’appel en pause dans ce salon", + "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Ajoute (╯°□°)╯︵ ┻━┻ en préfixe du message", + "Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message": "Ajoute ┬──┬ ノ( ゜-゜ノ) en préfixe du message", "Effects": "Effets", "Zimbabwe": "Zimbabwe", "Send images as you in your active room": "Envoie des images sous votre nom dans le salon actuel", "Send images as you in this room": "Envoie des images sous votre nom dans ce salon", - "See emotes posted to your active room": "Voir les émoticônes envoyées dans le salon actuel", - "See emotes posted to this room": "Voir les émoticônes envoyées dans ce salon", - "Send emotes as you in your active room": "Envoyer des émoticônes sous votre nom dans le salon actuel", - "Send emotes as you in this room": "Envoyer des émoticônes sous votre nom dans ce salon", - "See videos posted to your active room": "Voir les vidéos publiées dans votre salon actuel", + "See emotes posted to your active room": "Voir les réactions envoyées dans le salon actuel", + "See emotes posted to this room": "Voir les réactions envoyées dans ce salon", + "Send emotes as you in your active room": "Envoyer des réactions sous votre nom dans le salon actuel", + "Send emotes as you in this room": "Envoyer des réactions sous votre nom dans ce salon", + "See videos posted to your active room": "Voir les vidéos publiées dans votre salon actif", "See videos posted to this room": "Voir les vidéos publiées dans ce salon", - "Send videos as you in your active room": "Envoie des vidéos en tant que vous dans votre salon actuel", + "Send videos as you in your active room": "Envoie des vidéos en tant que vous-même dans votre salon actuel", "Send videos as you in this room": "Envoie des vidéos en tant que vous dans ce salon", "See images posted to this room": "Voir les images publiées dans ce salon", - "See images posted to your active room": "Voir les images publiées dans votre salon actuel", - "See messages posted to your active room": "Voir les messages publiés dans votre salon actuel", + "See images posted to your active room": "Voir les images publiées dans votre salon actif", + "See messages posted to your active room": "Voir les messages publiés dans votre salon actif", "See messages posted to this room": "Voir les messages publiés dans ce salon", - "Send messages as you in your active room": "Envoie des messages en tant que vous dans votre salon actuel", - "Send messages as you in this room": "Envoie des messages en tant que vous dans ce salon", + "Send messages as you in your active room": "Envoie des messages en tant que vous-même dans votre salon actif", + "Send messages as you in this room": "Envoie des messages en tant que vous-même dans ce salon", "The %(capability)s capability": "La capacité %(capability)s", "See %(eventType)s events posted to your active room": "Voir les événements %(eventType)s publiés dans votre salon actuel", - "Send %(eventType)s events as you in your active room": "Envoie des événements %(eventType)s en tant que vous dans votre salon actuel", + "Send %(eventType)s events as you in your active room": "Envoie des événements %(eventType)s en tant que vous-même dans votre salon actuel", "See %(eventType)s events posted to this room": "Voir les événements %(eventType)s publiés dans ce salon", - "Send %(eventType)s events as you in this room": "Envoie des événements %(eventType)s en tant que vous dans ce salon", - "Send stickers to your active room as you": "Envoie des stickers en tant que vous dans le salon actuel", + "Send %(eventType)s events as you in this room": "Envoie des événements %(eventType)s en tant que vous-même dans ce salon", + "Send stickers to your active room as you": "Envoie des stickers en tant que vous-même dans le salon actuel", "Continue with %(ssoButtons)s": "Continuer avec %(ssoButtons)s", "About homeservers": "À propos des serveurs d'accueils", "Learn more": "En savoir plus", @@ -2930,12 +2930,12 @@ "Don't miss a reply": "Ne ratez pas une réponse", "See %(msgtype)s messages posted to your active room": "Voir les messages de type %(msgtype)s publiés dans le salon actuel", "See %(msgtype)s messages posted to this room": "Voir les messages de type %(msgtype)s publiés dans ce salon", - "Send %(msgtype)s messages as you in this room": "Envoie des messages de type%(msgtype)s en tant que vous dans ce salon", - "Send %(msgtype)s messages as you in your active room": "Envoie des messages de type %(msgtype)s en tant que vous dans votre salon actuel", + "Send %(msgtype)s messages as you in this room": "Envoie des messages de type%(msgtype)s en tant que vous-même dans ce salon", + "Send %(msgtype)s messages as you in your active room": "Envoie des messages de type %(msgtype)s en tant que vous-même dans votre salon actif", "See general files posted to your active room": "Voir les fichiers postés dans votre salon actuel", "See general files posted to this room": "Voir les fichiers postés dans ce salon", - "Send general files as you in your active room": "Envoie des fichiers en tant que vous dans votre salon actuel", - "Send general files as you in this room": "Envoie des fichiers en tant que vous dans ce salon", + "Send general files as you in your active room": "Envoyer des fichiers en tant que vous-même dans votre salon actif", + "Send general files as you in this room": "Envoyer des fichiers en tant que vous-même dans ce salon", "Search (must be enabled)": "Recherche (si activée)", "This session has detected that your Security Phrase and key for Secure Messages have been removed.": "Cette session a détecté que votre phrase de passe et clé de sécurité pour les messages sécurisés ont été supprimées.", "A new Security Phrase and key for Secure Messages have been detected.": "Une nouvelle phrase de passe et clé pour les messages sécurisés ont été détectées.", @@ -3035,10 +3035,10 @@ "See when the name changes in your active room": "Suivre les changements de nom dans le salon actif", "Change which room, message, or user you're viewing": "Changer le salon, message, ou la personne que vous visualisez", "Change which room you're viewing": "Changer le salon que vous visualisez", - "Remain on your screen while running": "Restez sur votre écran pendant l’exécution", + "Remain on your screen while running": "Reste sur votre écran pendant l’exécution", "%(senderName)s has updated the widget layout": "%(senderName)s a mis à jour la disposition du widget", - "Converts the DM to a room": "Transformer le message privé en salon", - "Converts the room to a DM": "Transformer le salon en message privé", + "Converts the DM to a room": "Transforme le message privé en salon", + "Converts the room to a DM": "Transforme le salon en message privé", "Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "Votre serveur d’accueil a rejeté la demande de connexion. Ceci pourrait être dû à une connexion qui prend trop de temps. Si cela persiste, merci de contacter l’administrateur de votre serveur d’accueil.", "Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "Votre serveur d’accueil n’est pas accessible, nous n’avons pas pu vous connecter. Merci de réessayer. Si cela persiste, merci de contacter l’administrateur de votre serveur d’accueil.", "Try again": "Réessayez", From 83d10b827b0f75decc8959adb7c833fee5406afe Mon Sep 17 00:00:00 2001 From: Jean-Luc KABORE-TURQUIN Date: Thu, 25 Feb 2021 08:15:23 +0000 Subject: [PATCH 146/863] Translated using Weblate (French) Currently translated at 99.2% (2758 of 2780 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 304599cd44..3d024f6e18 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -77,7 +77,7 @@ "Email": "E-mail", "Failed to unban": "Échec de la révocation du bannissement", "Failed to verify email address: make sure you clicked the link in the email": "La vérification de l’adresse e-mail a échoué : vérifiez que vous avez bien cliqué sur le lien dans l’e-mail", - "Failure to create room": "Échec de la création du salon", + "Failure to create room": "Échec de création du salon", "Favourites": "Favoris", "Fill screen": "Plein écran", "Filter room members": "Filtrer les membres du salon", From f75d98493d0fba3883cf355746d63e2ee6df6bba Mon Sep 17 00:00:00 2001 From: Szimszon Date: Wed, 24 Feb 2021 21:44:12 +0000 Subject: [PATCH 147/863] Translated using Weblate (Hungarian) Currently translated at 100.0% (2780 of 2780 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 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 398dd04ec2..d0f8cc3b69 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -3060,5 +3060,27 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "A matrix szerverhez való csatlakozás nem sikerült. Zárja be ezt az ablakot és próbálja újra.", "Abort": "Megszakítás", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Biztos benne, hogy meg kívánja szakítani a gazdagép létrehozásának a folyamatát? A folyamat nem folytatható.", - "Confirm abort of host creation": "Erősítse meg a gazdagép készítés megszakítását" + "Confirm abort of host creation": "Erősítse meg a gazdagép készítés megszakítását", + "Upgrade to %(hostSignupBrand)s": "Frissítés erre: %(hostSignupBrand)s", + "Edit Values": "Értékek szerkesztése", + "Values at explicit levels in this room:": "Egyedi szinthez tartozó értékek ebben a szobában:", + "Values at explicit levels:": "Egyedi szinthez tartozó értékek:", + "Value in this room:": "Érték ebben a szobában:", + "Value:": "Érték:", + "Save setting values": "Beállított értékek mentése", + "Values at explicit levels in this room": "Egyedi szinthez tartozó értékek ebben a szobában", + "Values at explicit levels": "Egyedi szinthez tartozó értékek", + "Settable at room": "Szobára beállítható", + "Settable at global": "Általánosan beállítható", + "Level": "Szint", + "Setting definition:": "Beállítás leírása:", + "This UI does NOT check the types of the values. Use at your own risk.": "Ez a felület nem ellenőrzi az érték típusát. Csak saját felelősségére használja.", + "Caution:": "Figyelmeztetés:", + "Setting:": "Beállítás:", + "Value in this room": "Érték ebben a szobában", + "Value": "Érték", + "Setting ID": "Beállítás azon.", + "Failed to save settings": "A beállítások elmentése nem sikerült", + "Settings Explorer": "Beállítás Böngésző", + "Show chat effects (animations when receiving e.g. confetti)": "Csevegés effektek megjelenítése (mint a konfetti animáció)" } From 32ac7d7c8b98e26872bef1b707d05187d89405f4 Mon Sep 17 00:00:00 2001 From: jelv Date: Thu, 25 Feb 2021 13:56:38 +0000 Subject: [PATCH 148/863] Translated using Weblate (Dutch) Currently translated at 100.0% (2780 of 2780 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/nl/ --- src/i18n/strings/nl.json | 44 ++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 101d997d9c..0d80e520c8 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -689,7 +689,7 @@ "Messages in one-to-one chats": "Berichten in één-op-één gesprekken", "Unavailable": "Niet beschikbaar", "View Decrypted Source": "Ontsleutelde bron bekijken", - "Failed to update keywords": "Bijwerken van trefwoorden is mislukt", + "Failed to update keywords": "Updaten van trefwoorden is mislukt", "remove %(name)s from the directory.": "verwijder %(name)s uit de catalogus.", "Notifications on the following keywords follow rules which can’t be displayed here:": "Meldingen op de volgende trefwoorden volgen regels die hier niet getoond kunnen worden:", "Please set a password!": "Stel een wachtwoord in!", @@ -740,7 +740,7 @@ "What's new?": "Wat is er nieuw?", "Notify me for anything else": "Stuur een melding voor al het andere", "When I'm invited to a room": "Wanneer ik uitgenodigd word in een gesprek", - "Can't update user notification settings": "Kan de meldingsinstellingen van de gebruiker niet bijwerken", + "Can't update user notification settings": "Kan de meldingsinstellingen van de gebruiker niet updaten", "Notify for all other messages/rooms": "Stuur een melding voor alle andere berichten/gesprekken", "Unable to look up room ID from server": "Kon de gesprek-ID niet van de server ophalen", "Couldn't find a matching Matrix room": "Kon geen bijbehorend Matrix-gesprek vinden", @@ -852,7 +852,7 @@ "Gets or sets the room topic": "Verkrijgt het onderwerp van het gesprek of stelt het in", "This room has no topic.": "Dit gesprek heeft geen onderwerp.", "Sets the room name": "Stelt de gespreksnaam in", - "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s heeft dit gesprek bijgewerkt.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s heeft dit gesprek geüpgraded.", "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s heeft het gesprek toegankelijk gemaakt voor iedereen die de koppeling kent.", "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s heeft het gesprek enkel op uitnodiging toegankelijk gemaakt.", "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s heeft de toegangsregel veranderd naar ‘%(rule)s’", @@ -1240,7 +1240,7 @@ "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s heeft de uitnodiging aan %(targetDisplayName)s toe te treden tot het gesprek ingetrokken.", "Upgrade this room to the recommended room version": "Werk dit gesprek bij tot de aanbevolen versie", "This room is running room version , which this homeserver has marked as unstable.": "Dit gesprek draait op groepsgespreksversie , die door deze homeserver als onstabiel is gemarkeerd.", - "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Bijwerken zal de huidige versie van dit gesprek sluiten, en onder dezelfde naam een bijgewerkte versie starten.", + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgraden zal de huidige versie van dit gesprek sluiten, en onder dezelfde naam een geüpgraded versie starten.", "Failed to revoke invite": "Intrekken van uitnodiging is mislukt", "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Kon de uitnodiging niet intrekken. De server ondervindt mogelijk een tijdelijk probleem, of u heeft niet het recht de uitnodiging in te trekken.", "Revoke invite": "Uitnodiging intrekken", @@ -1289,7 +1289,7 @@ "Unexpected error resolving homeserver configuration": "Onverwachte fout bij het controleren van de homeserverconfiguratie", "The user's homeserver does not support the version of the room.": "De homeserver van de gebruiker biedt geen ondersteuning voor de gespreksversie.", "Show hidden events in timeline": "Verborgen gebeurtenissen op de tijdslijn weergeven", - "When rooms are upgraded": "Wanneer gesprekken bijgewerkt worden", + "When rooms are upgraded": "Wanneer gesprekken geüpgraded worden", "this room": "dit gesprek", "View older messages in %(roomName)s.": "Bekijk oudere berichten in %(roomName)s.", "Joining room …": "Deelnemen aan gesprek…", @@ -1316,7 +1316,7 @@ "This room doesn't exist. Are you sure you're at the right place?": "Dit gesprek bestaat niet. Weet u zeker dat u zich op de juiste plaats bevindt?", "Try again later, or ask a room admin to check if you have access.": "Probeer het later opnieuw, of vraag een gespreksbeheerder om te controleren of u wel toegang heeft.", "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "De foutcode %(errcode)s is weergegeven bij het toetreden van het gesprek. Als u meent dat u dit bericht foutief te zien krijgt, gelieve dan een foutmelding in te dienen.", - "This room has already been upgraded.": "Dit gesprek is reeds bijgewerkt.", + "This room has already been upgraded.": "Dit gesprek is reeds geüpgraded.", "reacted with %(shortName)s": "heeft gereageerd met %(shortName)s", "edited": "bewerkt", "Rotate Left": "Links draaien", @@ -1557,7 +1557,7 @@ "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.": "Dit vergt validatie van een e-mailadres of telefoonnummer middels de standaardidentiteitsserver , maar die server heeft geen gebruiksvoorwaarden.", "Trust": "Vertrouwen", "Custom (%(level)s)": "Aangepast (%(level)s)", - "Error upgrading room": "Bijwerken van gesprek mislukt", + "Error upgrading room": "Upgraden van gesprek mislukt", "Double check that your server supports the room version chosen and try again.": "Ga nogmaals na dat de server de gekozen gespreksversie ondersteunt, en probeer het dan opnieuw.", "Verifies a user, session, and pubkey tuple": "Verifieert een combinatie van gebruiker+sessie+publieke sleutel", "Unknown (user, session) pair:": "Onbekende combinatie gebruiker+sessie:", @@ -1618,7 +1618,7 @@ "Lock": "Hangslot", "Verify yourself & others to keep your chats safe": "Verifieer uzelf en anderen om uw gesprekken veilig te houden", "Other users may not trust it": "Mogelijk wantrouwen anderen het", - "Upgrade": "Bijwerken", + "Upgrade": "Upgraden", "Verify": "Verifiëren", "Later": "Later", "Review": "Controle", @@ -2377,7 +2377,7 @@ "Use between %(min)s pt and %(max)s pt": "Gebruik een getal tussen %(min)s pt en %(max)s pt", "Custom font size can only be between %(min)s pt and %(max)s pt": "Aangepaste lettergrootte kan alleen een getal tussen %(min)s pt en %(max)s pt zijn", "Size must be a number": "Grootte moet een getal zijn", - "New version available. Update now.": "Nieuwe versie beschikbaar. Nu bijwerken.", + "New version available. Update now.": "Nieuwe versie beschikbaar. Nu updaten.", "not ready": "Niet gereed", "ready": "Gereed", "unexpected type": "Onverwacht type", @@ -2445,7 +2445,7 @@ "The person who invited you already left the room, or their server is offline.": "De persoon door wie u ben uitgenodigd heeft het gesprek al verlaten, of hun server is offline.", "The person who invited you already left the room.": "De persoon door wie u ben uitgenodigd, heeft het gesprek reeds verlaten.", "New version of %(brand)s is available": "Nieuwe versie van %(brand)s is beschikbaar", - "Update %(brand)s": "%(brand)s bijwerken", + "Update %(brand)s": "%(brand)s updaten", "%(senderName)s has updated the widget layout": "%(senderName)s heeft de widget-indeling bijgewerkt", "%(senderName)s declined the call.": "%(senderName)s heeft de oproep afgewezen.", "(an error occurred)": "(een fout is opgetreden)", @@ -2950,5 +2950,27 @@ "Create a Group Chat": "Maak een groepsgesprek aan", "Send a Direct Message": "Start een direct gesprek", "Welcome to %(appName)s": "Welkom bij %(appName)s", - "Add a topic to help people know what it is about.": "Stel een gespreksonderwerp in zodat mensen weten waar het over gaat." + "Add a topic to help people know what it is about.": "Stel een gespreksonderwerp in zodat mensen weten waar het over gaat.", + "Upgrade to %(hostSignupBrand)s": "Upgrade naar %(hostSignupBrand)s", + "Edit Values": "Waarde wijzigen", + "Values at explicit levels in this room:": "Waarde op expliciete niveaus in dit gesprek:", + "Values at explicit levels:": "Waardes op expliciete niveaus:", + "Value in this room:": "Waarde in dit gesprek:", + "Value:": "Waarde:", + "Save setting values": "Instelling waardes opslaan", + "Values at explicit levels in this room": "Waardes op expliciete niveaus in dit gesprek", + "Values at explicit levels": "Waardes op expliciete niveaus", + "Settable at room": "Instelbaar op gesprek", + "Settable at global": "Instelbaar op globaal", + "Level": "Niveau", + "Setting definition:": "Instelling definitie:", + "This UI does NOT check the types of the values. Use at your own risk.": "De UI heeft GEEN controle op het type van de waardes. Gebruik op eigen risico.", + "Caution:": "Opgelet:", + "Setting:": "Instelling:", + "Value in this room": "Waarde van dit gesprek", + "Value": "Waarde", + "Setting ID": "Instellingen-ID", + "Failed to save settings": "Kan geen instellingen opslaan", + "Settings Explorer": "Instellingen Ontdekken", + "Show chat effects (animations when receiving e.g. confetti)": "Effecten tonen (animaties bij ontvangst bijv. confetti)" } From abbe51660438ad2baa92d88097a2c1f926ab71b9 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Fri, 26 Feb 2021 17:32:14 +0000 Subject: [PATCH 149/863] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2780 of 2780 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 | 38 ++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index b32fbf5094..ef748c5fd8 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -2983,5 +2983,41 @@ "Converts the DM to a room": "Converte a conversa para uma sala", "Converts the room to a DM": "Converte a sala para uma conversa", "Try again": "Tente novamente", - "We couldn't log you in": "Não foi possível fazer login" + "We couldn't log you in": "Não foi possível fazer login", + "%(hostSignupBrand)s Setup": "Configuração do %(hostSignupBrand)s", + "Continuing temporarily allows the %(hostSignupBrand)s setup process to access your account to fetch verified email addresses. This data is not stored.": "Ao continuar, você permite que o processo de configuração do %(hostSignupBrand)s acesse a sua conta para obter endereços de e-mail verificados, temporariamente. Esses dados não são armazenados.", + "Settable at room": "Definido em cada sala", + "Settable at global": "Definido globalmente", + "Settings Explorer": "Explorador de configurações", + "Level": "Nível", + "Setting definition:": "Definição da configuração:", + "Setting ID": "ID da configuração", + "Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "Seu servidor local recusou a sua tentativa de login. Isso pode ocorrer quando a conexão de internet estiver demorando muito. Por favor, tente novamente. Se o problema continuar, entre em contato com o administrador do seu servidor local.", + "Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "O seu servidor local está inacessível e não foi possível fazer o seu login. Tente novamente. Se o problema continuar, entre em contato com o administrador do seu servidor local.", + "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Anteriormente, pedimos ao seu navegador para lembrar qual servidor local você usa para fazer login, mas infelizmente o navegador se esqueceu disso. Vá para a página de login e tente novamente.", + "Upgrade to %(hostSignupBrand)s": "Atualizar para %(hostSignupBrand)s", + "Minimize dialog": "Minimizar a janela", + "Maximize dialog": "Maximizar a janela", + "You should know": "Você deveria saber", + "Cookie Policy": "Política de cookies", + "Failed to connect to your homeserver. Please close this dialog and try again.": "Falha ao se conectar ao seu servidor local. Feche esta caixa de diálogo e tente novamente.", + "Abort": "Cancelar", + "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Tem certeza de que deseja cancelar a criação do host? O processo não pode ser continuado.", + "Confirm abort of host creation": "Confirmar o cancelamento da criação do host", + "Edit Values": "Editar valores", + "Values at explicit levels in this room:": "Valores em níveis explícitos nessa sala:", + "Values at explicit levels:": "Valores em níveis explícitos:", + "Value in this room:": "Valor nessa sala:", + "Value:": "Valor:", + "Save setting values": "Salvar valores de configuração", + "Values at explicit levels in this room": "Valores em níveis explícitos nessa sala", + "Values at explicit levels": "Valores em níveis explícitos", + "This UI does NOT check the types of the values. Use at your own risk.": "Esta interface de usuário NÃO verifica os tipos de valores. Use por sua conta e risco.", + "Caution:": "Atenção:", + "Setting:": "Configuração:", + "Value in this room": "Valor nessa sala", + "Value": "Valor", + "Failed to save settings": "Falha ao salvar as configurações", + "Show chat effects (animations when receiving e.g. confetti)": "Mostrar efeitos na conversa (por exemplo: animações ao receber confetes)", + "Element Web is experimental on mobile. For a better experience and the latest features, use our free native app.": "O Element Web é experimental para dispositivos móveis. Para uma melhor experiência e os recursos mais recentes, use nosso aplicativo gratuito." } From 3540bec046cc648f97699967439d81b1c5ed2027 Mon Sep 17 00:00:00 2001 From: rkfg Date: Sat, 27 Feb 2021 11:02:52 +0000 Subject: [PATCH 150/863] Translated using Weblate (Russian) Currently translated at 99.6% (2769 of 2780 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 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 65aeaf82d9..8310676e69 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -1591,7 +1591,7 @@ "Error upgrading room": "Ошибка обновления комнаты", "Match system theme": "Тема системы", "Show tray icon and minimize window to it on close": "Показать иконку в трее и сворачивать окно при закрытии", - "Show typing notifications": "Показывать уведомления о наборе", + "Show typing notifications": "Показывать уведомления о наборе текста", "Delete %(count)s sessions|other": "Удалить %(count)s сессий", "Enable desktop notifications for this session": "Включить уведомления для рабочего стола для этой сессии", "Enable audible notifications for this session": "Включить звуковые уведомления для этой сессии", @@ -3031,7 +3031,7 @@ "Privacy Policy": "Политика конфиденциальности", "Cookie Policy": "Политика использования файлов cookie", "Learn more in our , and .": "Дополнительную информацию можно найти на страницах , и .", - "Continuing temporarily allows the %(hostSignupBrand)s setup process to access your account to fetch verified email addresses. This data is not stored.": "Продолжая процесс настройки %(hostSignupBrand)s, вы предоставите доступ к вашей учётной записи для получения проверенных адресов электронной почты. Эти данные не сохраняются.", + "Continuing temporarily allows the %(hostSignupBrand)s setup process to access your account to fetch verified email addresses. This data is not stored.": "Продолжая процесс настройки %(hostSignupBrand)s, вы предоставите временный доступ к вашей учётной записи для получения проверенных адресов электронной почты. Эти данные не сохраняются.", "Failed to connect to your homeserver. Please close this dialog and try again.": "Не удалось подключиться к домашнему серверу. Закройте это диалоговое окно и попробуйте ещё раз.", "Abort": "Отмена", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Вы уверены, что хотите прервать создание хоста? Процесс не может быть продолжен.", @@ -3072,5 +3072,7 @@ "Value in this room": "Значение в этой комнате", "Value": "Значение", "Failed to save settings": "Не удалось сохранить настройки", - "Show chat effects (animations when receiving e.g. confetti)": "Показать эффекты чата (анимация при получении, например, конфетти)" + "Show chat effects (animations when receiving e.g. confetti)": "Показать эффекты чата (анимация при получении, например, конфетти)", + "Caution:": "Предупреждение:", + "Settings Explorer": "Обзор настроек" } From 8d68cbdb142ab0f9f513d1b3583490824cbb49be Mon Sep 17 00:00:00 2001 From: A-l-exa-n-d-r Date: Sat, 27 Feb 2021 08:05:08 +0000 Subject: [PATCH 151/863] Translated using Weblate (Russian) Currently translated at 99.6% (2769 of 2780 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 8310676e69..7cbcb08167 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -3068,7 +3068,7 @@ "Settable at room": "Устанавливается для комнаты", "Settable at global": "Устанавливается на глобальном уровне", "Level": "Уровень", - "This UI does NOT check the types of the values. Use at your own risk.": "Этот пользовательский интерфейс НЕ проверяет типы значений. Используйте на свой риск.", + "This UI does NOT check the types of the values. Use at your own risk.": "Этот пользовательский интерфейс НЕ проверяет типы значений. Используйте на свой страх и риск.", "Value in this room": "Значение в этой комнате", "Value": "Значение", "Failed to save settings": "Не удалось сохранить настройки", From b6d4e3fe9a247835d87337282ebc2eba7af26a20 Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Fri, 26 Feb 2021 07:13:31 +0000 Subject: [PATCH 152/863] Translated using Weblate (Swedish) Currently translated at 100.0% (2780 of 2780 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 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sv.json b/src/i18n/strings/sv.json index 916de9ecce..c8a5a41339 100644 --- a/src/i18n/strings/sv.json +++ b/src/i18n/strings/sv.json @@ -2996,5 +2996,27 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "Misslyckades att ansluta till din hemserver. Vänligen stäng den här dialogrutan och försök igen.", "Abort": "Avbryt", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Är du säker på att du vill avbryta skapande av värden? Processen kan inte fortsättas.", - "Confirm abort of host creation": "Bekräfta avbrytning av värdskapande" + "Confirm abort of host creation": "Bekräfta avbrytning av värdskapande", + "Upgrade to %(hostSignupBrand)s": "Uppgradera till %(hostSignupBrand)s", + "Edit Values": "Redigera värden", + "Values at explicit levels in this room:": "Värden vid explicita nivåer i det här rummet:", + "Values at explicit levels:": "Värden vid explicita nivåer:", + "Value in this room:": "Värde i det här rummet:", + "Value:": "Värde:", + "Save setting values": "Spara inställningsvärden", + "Values at explicit levels in this room": "Värden vid explicita nivåer i det här rummet", + "Values at explicit levels": "Värden vid explicita nivåer", + "Settable at room": "Inställningsbar per rum", + "Settable at global": "Inställningsbar globalt", + "Level": "Nivå", + "Setting definition:": "Inställningsdefinition:", + "This UI does NOT check the types of the values. Use at your own risk.": "Det här UI:t kontrollerar inte typer för värden. Använd på egen risk.", + "Caution:": "Varning:", + "Setting:": "Inställning:", + "Value in this room": "Värde i det här rummet", + "Value": "Värde", + "Setting ID": "Inställnings-ID", + "Failed to save settings": "Misslyckades att spara inställningar", + "Settings Explorer": "Inställningsutforskare", + "Show chat effects (animations when receiving e.g. confetti)": "Visa chatteffekter (animeringar när du tar emot t.ex. konfetti)" } From ad38f87287b13eb2ef835781287c16f26169a204 Mon Sep 17 00:00:00 2001 From: linsui Date: Fri, 26 Feb 2021 14:45:43 +0000 Subject: [PATCH 153/863] Translated using Weblate (Chinese (Simplified)) Currently translated at 80.7% (2245 of 2780 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ --- src/i18n/strings/zh_Hans.json | 75 ++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 167fea70e1..12fb5e2877 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -2409,5 +2409,78 @@ "The call was answered on another device.": "在另一台设备上应答了该通话。", "The call could not be established": "无法建立通话", "The other party declined the call.": "对方拒绝了通话。", - "Call Declined": "通话被拒绝" + "Call Declined": "通话被拒绝", + "(connection failed)": "(连接失败)", + "🎉 All servers are banned from participating! This room can no longer be used.": "🎉 所有服务器都已禁止参与!此聊天室不再可用。", + "%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s 为此聊天室更改了服务器 ACL。", + "%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s 为此聊天室设置了服务器 ACL。", + "Hong Kong": "香港", + "Cook Islands": "库克群岛", + "Congo - Kinshasa": "刚果金", + "Congo - Brazzaville": "刚果布拉柴维尔", + "Comoros": "科摩罗", + "Colombia": "哥伦比亚", + "Cocos (Keeling) Islands": "科科斯基林群岛", + "Christmas Island": "圣诞岛", + "China": "中国", + "Chile": "智利", + "Chad": "乍得", + "Central African Republic": "中非共和国", + "Cayman Islands": "开曼群岛(英)", + "Caribbean Netherlands": "荷兰加勒比区", + "Cape Verde": "佛得角", + "Canada": "加拿大", + "Cameroon": "喀麦隆", + "Cambodia": "柬埔寨", + "Burundi": "布隆迪", + "Burkina Faso": "布基纳法索", + "Bulgaria": "保加利亚", + "Brunei": "文莱", + "British Virgin Islands": "英属维尔京群岛", + "British Indian Ocean Territory": "英属印度洋领地", + "Brazil": "巴西", + "Bouvet Island": "布维岛", + "Botswana": "博茨瓦纳", + "Bosnia": "波斯尼亚", + "Bolivia": "玻利维亚", + "Armenia": "亚美尼亚", + "Bhutan": "不丹", + "Bermuda": "百慕大群岛", + "Benin": "贝宁", + "Belize": "伯利兹", + "Belgium": "比利时", + "Belarus": "白俄罗斯", + "Barbados": "巴巴多斯", + "Bangladesh": "孟加拉国", + "Bahrain": "巴林", + "Bahamas": "巴哈马", + "Azerbaijan": "阿塞拜疆", + "Austria": "奥地利", + "Australia": "澳大利亚", + "Aruba": "阿鲁巴岛", + "Argentina": "阿根廷", + "Antigua & Barbuda": "安提瓜和巴布达", + "Antarctica": "南极洲", + "Anguilla": "安圭拉", + "Angola": "安哥拉", + "Andorra": "安道尔", + "American Samoa": "美属萨摩亚", + "Algeria": "阿尔及利亚", + "Albania": "阿尔巴尼亚", + "Åland Islands": "奥兰群岛", + "Afghanistan": "阿富汗", + "United States": "美国", + "United Kingdom": "英国", + "Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "您的主服务器已拒绝您的登入尝试。请重试。如果此情况持续发生,请联系您的主服务器管理员。", + "Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "您的主服务器不可达,无法使您登入。请重试。如果此情况持续发生,请联系您的主服务器管理员。", + "Try again": "重试", + "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "我们已要求浏览器记住您使用的主服务器,但不幸的是您的浏览器已忘记。请前往登录页面重试。", + "We couldn't log you in": "我们无法使您登入", + "This will end the conference for everyone. Continue?": "这将结束所有人的会议。是否继续?", + "End conference": "结束会议", + "You've reached the maximum number of simultaneous calls.": "您已达到并行呼叫最大数量。", + "Too Many Calls": "太多呼叫", + "Call failed because webcam or microphone could not be accessed. Check that:": "通话失败,因为无法访问网络摄像头或麦克风。请检查:", + "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "呼叫失败,因为无法访问任何麦克风。 检查是否已插入麦克风并正确设置。", + "Answered Elsewhere": "在其他地方已回答" } From c4940d164381449f857a5c88cc9abbc62939a6aa Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 25 Feb 2021 01:41:18 +0000 Subject: [PATCH 154/863] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2780 of 2780 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 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index b11ff2274f..64dab74139 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -3068,5 +3068,27 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "無法連線到您的家伺服器。請關閉對話框並再試一次。", "Abort": "中止", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "您確定您想要中止主機建立嗎?流程將無法繼續。", - "Confirm abort of host creation": "確認中止主機建立" + "Confirm abort of host creation": "確認中止主機建立", + "Upgrade to %(hostSignupBrand)s": "升級至 %(hostSignupBrand)s", + "Edit Values": "編輯值", + "Values at explicit levels in this room:": "此聊天室中明確等級的值:", + "Values at explicit levels:": "明確等級的值:", + "Value in this room:": "此聊天室中的值:", + "Value:": "值:", + "Save setting values": "儲存設定值", + "Values at explicit levels in this room": "此聊天室中明確等級的值", + "Values at explicit levels": "明確等級的值", + "Settable at room": "聊天室設定", + "Settable at global": "全域設定", + "Level": "等級", + "Setting definition:": "設定定義:", + "This UI does NOT check the types of the values. Use at your own risk.": "此使用者介面不會檢查值的類型。使用風險自負。", + "Caution:": "警告:", + "Setting:": "設定:", + "Value in this room": "此聊天室中的值", + "Value": "值", + "Setting ID": "設定 ID", + "Failed to save settings": "儲存設定失敗", + "Settings Explorer": "設定瀏覽程式", + "Show chat effects (animations when receiving e.g. confetti)": "顯示聊天效果(當收到如五彩紙屑時顯示動畫)" } From 66cf2075fe9457f5190eb43723efc6cefd327285 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Fri, 26 Feb 2021 08:30:59 +0000 Subject: [PATCH 155/863] Translated using Weblate (Czech) Currently translated at 100.0% (2780 of 2780 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ --- src/i18n/strings/cs.json | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 3ee9c73399..47a99ab670 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -231,7 +231,7 @@ "You do not have permission to do that in this room.": "V této místnosti nemáte na toto právo.", "You cannot place a call with yourself.": "Nemůžete volat sami sobě.", "You cannot place VoIP calls in this browser.": "V tomto prohlížeči nelze vytáčet VoIP hovory.", - "You do not have permission to post to this room": "Na přispívání do této místnosti nemáte právo", + "You do not have permission to post to this room": "Nemáte oprávnění zveřejňovat příspěvky v této místnosti", "Online": "Online", "Offline": "Offline", "Check for update": "Zkontrolovat aktualizace", @@ -1409,7 +1409,7 @@ "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Odeslat pozvánku pomocí serveru identit. Použít výchozí (%(defaultIdentityServerName)s) nebo přenastavit Nastavení.", "Use an identity server to invite by email. Manage in Settings.": "Odeslat pozvánku pomocí serveru identit. Přenastavit v Nastavení.", "Close dialog": "Zavřít dialog", - "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Napište nám prosím co se pokazilo a nebo nám napište issue na GitHub, kde popíšete problém.", + "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Dejte nám vědět, prosím, co se pokazilo nebo vytvořte issue na GitHubu, kde problém popište.", "Removing…": "Odstaňování…", "Clear all data": "Smazat všechna data", "Please enter a name for the room": "Zadejte prosím název místnosti", @@ -1529,7 +1529,7 @@ "Flags": "Vlajky", "Quick Reactions": "Rychlé reakce", "Cancel search": "Zrušit hledání", - "Please create a new issue on GitHub so that we can investigate this bug.": "Vyrobte prosím nové issue na GitHubu abychom mohli chybu opravit.", + "Please create a new issue on GitHub so that we can investigate this bug.": "Vytvořte prosím novou issue na GitHubu abychom mohli chybu opravit.", "%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)s neudělali %(count)s krát žádnou změnu", "%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)s neudělali žádnou změnu", "%(oneUser)smade no changes %(count)s times|other": "%(oneUser)s neudělal(a) %(count)s krát žádnou změnu", @@ -2981,5 +2981,27 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "Připojení k domovskému serveru se nezdařilo. Zavřete toto dialogové okno a zkuste to znovu.", "Abort": "Přerušit", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Opravdu chcete přerušit vytváření hostitele? Proces nemůže být navázán.", - "Confirm abort of host creation": "Potvrďte přerušení vytváření hostitele" + "Confirm abort of host creation": "Potvrďte přerušení vytváření hostitele", + "Upgrade to %(hostSignupBrand)s": "Aktualizovat na %(hostSignupBrand)s", + "Edit Values": "Upravit hodnoty", + "Values at explicit levels in this room:": "Hodnoty na explicitních úrovních v této místnosti:", + "Values at explicit levels:": "Hodnoty na explicitních úrovních:", + "Value in this room:": "Hodnota v této místnosti:", + "Value:": "Hodnota:", + "Save setting values": "Uložit hodnoty nastavení", + "Values at explicit levels in this room": "Hodnoty na explicitních úrovních v této místnosti", + "Values at explicit levels": "Hodnoty na explicitních úrovních", + "Settable at room": "Nastavitelné v místnosti", + "Settable at global": "Nastavitelné na globální úrovni", + "Level": "Úroveň", + "Setting definition:": "Definice nastavení:", + "This UI does NOT check the types of the values. Use at your own risk.": "Toto uživatelské rozhraní NEKONTROLUJE typy hodnot. Použití na vlastní nebezpečí.", + "Caution:": "Pozor:", + "Setting:": "Nastavení:", + "Value in this room": "Hodnota v této místnosti", + "Value": "Hodnota", + "Setting ID": "ID nastavení", + "Failed to save settings": "Nastavení se nepodařilo uložit", + "Settings Explorer": "Průzkumník nastavení", + "Show chat effects (animations when receiving e.g. confetti)": "Zobrazit efekty chatu (animace např. při přijetí konfet)" } From b6a5b08a86cb466d7e11982c1f0b5a91c53bb5ae Mon Sep 17 00:00:00 2001 From: XoseM Date: Thu, 25 Feb 2021 06:09:06 +0000 Subject: [PATCH 156/863] Translated using Weblate (Galician) Currently translated at 100.0% (2780 of 2780 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 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index b309f12245..98a984d860 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -3065,5 +3065,27 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "Fallou a conexión co teu servidor de inicio. Pecha esta información e inténtao outra vez.", "Abort": "Abortar", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Tes a certeza de querer cancelar a creación do servidor? O proceso non pode ser completado.", - "Confirm abort of host creation": "Corfirma que cancelas a creación do servidor" + "Confirm abort of host creation": "Corfirma que cancelas a creación do servidor", + "Upgrade to %(hostSignupBrand)s": "Actualizar a %(hostSignupBrand)s", + "Edit Values": "Editar valores", + "Values at explicit levels in this room:": "Valores a niveis explícitos nesta sala:", + "Values at explicit levels:": "Valores a niveis explícitos:", + "Value in this room:": "Valor nesta sala:", + "Value:": "Valor:", + "Save setting values": "Gardar valores configurados", + "Values at explicit levels in this room": "Valores a niveis explícitos nesta sala", + "Values at explicit levels": "Valores e niveis explícitos", + "Settable at room": "Configurable na sala", + "Settable at global": "Configurable como global", + "Level": "Nivel", + "Setting definition:": "Definición do axuste:", + "This UI does NOT check the types of the values. Use at your own risk.": "Esta IU non comproba os tipos dos valores. Usa baixo a túa responsabilidade.", + "Caution:": "Aviso:", + "Setting:": "Axuste:", + "Value in this room": "Valor nesta sala", + "Value": "Valor", + "Setting ID": "ID do axuste", + "Failed to save settings": "Non se gardaron os axustes", + "Settings Explorer": "Navegar nos axustes", + "Show chat effects (animations when receiving e.g. confetti)": "Mostrar efectos no chat (animacións na recepción, ex. confetti)" } From 73421597b63b7f3f266f7fcf94bb4030c70b9a12 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 26 Feb 2021 17:16:44 +0000 Subject: [PATCH 157/863] Translated using Weblate (Albanian) Currently translated at 99.6% (2769 of 2780 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 | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index e8e47ac81b..dd58154b5a 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2990,7 +2990,7 @@ "If you've forgotten your Security Key you can ": "Nëse keni harruar Kyçin tuaj të Sigurisë, mund të .", "Your Security Key has been copied to your clipboard, paste it to:": "Kyçi juaj i Sigurisë është kopjuar te e papastra juaj, ngjiteni te:", "Confirm your Security Phrase": "Ripohoni Frazën tuaj të Sigurisë", - "Secure your backup with a Security Phrase": "Sigurojeni kopjeruajtjen tuaj me një Frazë Sigurie.", + "Secure your backup with a Security Phrase": "Sigurojeni kopjeruajtjen tuaj me një Frazë Sigurie", "Repeat your Security Phrase...": "Përsëritni Frazën tuaj të Sigurisë…", "Please enter your Security Phrase a second time to confirm.": "Ju lutemi, që të ripohohet, rijepeni Frazën tuaj të Sigurisë.", "This session has detected that your Security Phrase and key for Secure Messages have been removed.": "Ky sesion ka pikasur se Fraza e Sigurisë dhe kyçi juaj për Mesazhe të Sigurt janë hequr.", @@ -3056,5 +3056,25 @@ "Failed to connect to your homeserver. Please close this dialog and try again.": "S’u arrit të lidhej me shërbyesin tuaj Home. Ju lutemi, mbylleni këtë dialog dhe riprovoni.", "Abort": "Ndërprite", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Jeni i sigurt se doni të ndërpritet krijimi i strehës? Procesi s’mund të vazhdohet.", - "Confirm abort of host creation": "Ripohoni ndërprerjen e krijimit të strehës" + "Confirm abort of host creation": "Ripohoni ndërprerjen e krijimit të strehës", + "Upgrade to %(hostSignupBrand)s": "Përmirësojeni me %(hostSignupBrand)s", + "Edit Values": "Përpunoni Vlera", + "Values at explicit levels in this room:": "Vlera në nivele shprehimisht në këtë dhomë:", + "Values at explicit levels:": "Vlera në nivele shprehimisht:", + "Value in this room:": "Vlerë në këtë dhomë:", + "Value:": "Vlerë:", + "Save setting values": "Ruaj vlera rregullimi", + "Values at explicit levels in this room": "Vlera në nivele shprehimisht në këtë dhomë", + "Values at explicit levels": "Vlera në nivele shprehimisht", + "Settable at room": "I caktueshëm për dhomën", + "Settable at global": "I caktueshëm te të përgjithshmet", + "Level": "Nivel", + "Setting definition:": "Përkufizim rregullimi:", + "This UI does NOT check the types of the values. Use at your own risk.": "Kjo UI NUK kontrollon llojet e vlerave. Përdorini me përgjegjësinë tuaj.", + "Setting:": "Rregullim:", + "Value in this room": "Vlerë në këtë dhomë", + "Value": "Vlerë", + "Failed to save settings": "S’u arrit të ruhen rregullimet", + "Settings Explorer": "Eksplorues Rregullimesh", + "Show chat effects (animations when receiving e.g. confetti)": "Shfaq efekte fjalosjeje (animacione kur merren bonbone, për shembull)" } From 55bfe21df0ed8827b7dca40cd580ac91cf202f82 Mon Sep 17 00:00:00 2001 From: Trendyne Date: Thu, 25 Feb 2021 14:25:53 +0000 Subject: [PATCH 158/863] Translated using Weblate (Icelandic) Currently translated at 14.8% (413 of 2780 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/is/ --- src/i18n/strings/is.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/is.json b/src/i18n/strings/is.json index 6f561331f6..ddb3bbc66d 100644 --- a/src/i18n/strings/is.json +++ b/src/i18n/strings/is.json @@ -458,5 +458,7 @@ "Passphrases must match": "Lykilfrasar verða að stemma", "Passphrase must not be empty": "Lykilfrasi má ekki vera auður", "Create Account": "Stofna Reikning", - "Please install Chrome, Firefox, or Safari for the best experience.": "vinsamlegast setja upp Chrome, Firefox, eða Safari fyrir besta reynsluna." + "Please install Chrome, Firefox, or Safari for the best experience.": "vinsamlegast setja upp Chrome, Firefox, eða Safari fyrir besta reynsluna.", + "Explore rooms": "Kanna herbergi", + "Sign In": "Skrá inn" } From 36ef0be05b028a4e97afac0499491a20f9ae7ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 25 Feb 2021 16:43:43 +0000 Subject: [PATCH 159/863] Translated using Weblate (Estonian) Currently translated at 100.0% (2780 of 2780 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 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 7769cf61c8..eb895f5f26 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -3066,5 +3066,27 @@ "Privacy Policy": "Privaatsuspoliitika", "Cookie Policy": "Küpsiste kasutamine", "Learn more in our , and .": "Lisateavet leiad , ja lehtedelt.", - "Failed to connect to your homeserver. Please close this dialog and try again.": "Ei õnnestunud ühendada koduserveriga. Palun sulge see aken ja proovi uuesti." + "Failed to connect to your homeserver. Please close this dialog and try again.": "Ei õnnestunud ühendada koduserveriga. Palun sulge see aken ja proovi uuesti.", + "Upgrade to %(hostSignupBrand)s": "Kui soovid, siis võta kasutusele %(hostSignupBrand)s", + "Edit Values": "Muuda väärtusi", + "Values at explicit levels in this room:": "Väärtused konkreetsel tasemel selles jututoas:", + "Values at explicit levels:": "Väärtused konkreetsel tasemel:", + "Value in this room:": "Väärtus selles jututoas:", + "Value:": "Väärtus:", + "Save setting values": "Salvesta seadistuste väärtused", + "Values at explicit levels in this room": "Väärtused konkreetsel tasemel selles jututoas", + "Values at explicit levels": "Väärtused konkreetsel tasemel", + "Settable at room": "Seadistatav jututoa-kohaselt", + "Settable at global": "Seadistatav üldiselt", + "Level": "Tase", + "Setting definition:": "Seadistuse määratlus:", + "This UI does NOT check the types of the values. Use at your own risk.": "See kasutajaliides ei oska kontrollida väärtuste tüüpi ja vormingut. Muudatusi teed omal vastutusel.", + "Caution:": "Hoiatus:", + "Setting:": "Seadistus:", + "Value in this room": "Väärtus selles jututoas", + "Value": "Väärtus", + "Setting ID": "Seadistuse tunnus", + "Failed to save settings": "Seadistuste salvestamine ei õnnestunud", + "Settings Explorer": "Seadistuste haldur", + "Show chat effects (animations when receiving e.g. confetti)": "Näita vestluses edevat graafikat (näiteks kui keegi on saatnud serpentiine)" } From 601be50b7127518c86e891f933c31d861fd83abb Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Mon, 1 Mar 2021 21:43:00 +1300 Subject: [PATCH 160/863] Split KeyAction into multiple enums This gives some additional type safety and makes enum member usage more clear. --- src/KeyBindingsManager.ts | 254 +++++++++--------- src/components/structures/LoggedInView.tsx | 34 +-- src/components/structures/RoomSearch.tsx | 12 +- src/components/structures/RoomView.tsx | 10 +- .../views/rooms/BasicMessageComposer.tsx | 32 +-- .../views/rooms/EditMessageComposer.js | 12 +- src/components/views/rooms/RoomSublist.tsx | 8 +- .../views/rooms/SendMessageComposer.js | 16 +- 8 files changed, 185 insertions(+), 193 deletions(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index b969982bda..d8c128a2bf 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -1,24 +1,8 @@ import { isMac, Key } from './Keyboard'; import SettingsStore from './settings/SettingsStore'; -export enum KeyBindingContext { - /** Key bindings for the chat message composer component */ - MessageComposer = 'MessageComposer', - /** Key bindings for text editing autocompletion */ - AutoComplete = 'AutoComplete', - /** Left room list sidebar */ - RoomList = 'RoomList', - /** Current room view */ - Room = 'Room', - /** Shortcuts to navigate do various menus / dialogs / screens */ - Navigation = 'Navigation', -} - -export enum KeyAction { - None = 'None', - - // SendMessageComposer actions: - +/** Actions for the chat message composer component */ +export enum MessageComposerAction { /** Send a message */ Send = 'Send', /** Go backwards through the send history and use the message in composer view */ @@ -46,70 +30,74 @@ export enum KeyAction { NewLine = 'NewLine', MoveCursorToStart = 'MoveCursorToStart', MoveCursorToEnd = 'MoveCursorToEnd', +} - // Autocomplete - +/** Actions for text editing autocompletion */ +export enum AutocompleteAction { /** Apply the current autocomplete selection */ - AutocompleteApply = 'AutocompleteApply', + ApplySelection = 'ApplySelection', /** Cancel autocompletion */ - AutocompleteCancel = 'AutocompleteCancel', + Cancel = 'Cancel', /** Move to the previous autocomplete selection */ - AutocompletePrevSelection = 'AutocompletePrevSelection', + PrevSelection = 'PrevSelection', /** Move to the next autocomplete selection */ - AutocompleteNextSelection = 'AutocompleteNextSelection', - - // Room list + NextSelection = 'NextSelection', +} +/** Actions for the left room list sidebar */ +export enum RoomListAction { /** Clear room list filter field */ - RoomListClearSearch = 'RoomListClearSearch', + ClearSearch = 'ClearSearch', /** Navigate up/down in the room list */ - RoomListPrevRoom = 'RoomListPrevRoom', + PrevRoom = 'PrevRoom', /** Navigate down in the room list */ - RoomListNextRoom = 'RoomListNextRoom', + NextRoom = 'NextRoom', /** Select room from the room list */ - RoomListSelectRoom = 'RoomListSelectRoom', + SelectRoom = 'SelectRoom', /** Collapse room list section */ - RoomListCollapseSection = 'RoomListCollapseSection', + CollapseSection = 'CollapseSection', /** Expand room list section, if already expanded, jump to first room in the selection */ - RoomListExpandSection = 'RoomListExpandSection', + ExpandSection = 'ExpandSection', +} - // Room - - /** Jump to room search */ - RoomFocusRoomSearch = 'RoomFocusRoomSearch', +/** Actions for the current room view */ +export enum RoomAction { + /** Jump to room search (search for a room)*/ + FocusRoomSearch = 'FocusRoomSearch', // TODO: move to NavigationAction? /** Scroll up in the timeline */ - RoomScrollUp = 'RoomScrollUp', + ScrollUp = 'ScrollUp', /** Scroll down in the timeline */ RoomScrollDown = 'RoomScrollDown', /** Dismiss read marker and jump to bottom */ - RoomDismissReadMarker = 'RoomDismissReadMarker', + DismissReadMarker = 'DismissReadMarker', /* Upload a file */ - RoomUploadFile = 'RoomUploadFile', - /* Search (must be enabled) */ - RoomSearch = 'RoomSearch', + UploadFile = 'UploadFile', + /* Focus search message in a room (must be enabled) */ + FocusSearch = 'FocusSearch', /* Jump to the first (downloaded) message in the room */ - RoomJumpToFirstMessage = 'RoomJumpToFirstMessage', + JumpToFirstMessage = 'JumpToFirstMessage', /* Jump to the latest message in the room */ - RoomJumpToLatestMessage = 'RoomJumpToLatestMessage', - - // Navigation + JumpToLatestMessage = 'JumpToLatestMessage', +} +/** Actions for navigating do various menus / dialogs / screens */ +export enum NavigationAction { /** Toggle the room side panel */ - NavToggleRoomSidePanel = 'NavToggleRoomSidePanel', + ToggleRoomSidePanel = 'ToggleRoomSidePanel', /** Toggle the user menu */ - NavToggleUserMenu = 'NavToggleUserMenu', + ToggleUserMenu = 'ToggleUserMenu', /* Toggle the short cut help dialog */ - NavToggleShortCutDialog = 'NavToggleShortCutDialog', + ToggleShortCutDialog = 'ToggleShortCutDialog', /* Got to the Element home screen */ - NavGoToHome = 'NavGoToHome', + GoToHome = 'GoToHome', /* Select prev room */ - NavSelectPrevRoom = 'NavSelectPrevRoom', + SelectPrevRoom = 'SelectPrevRoom', /* Select next room */ - NavSelectNextRoom = 'NavSelectNextRoom', + SelectNextRoom = 'SelectNextRoom', /* Select prev room with unread messages*/ - NavSelectPrevUnreadRoom = 'NavSelectPrevUnreadRoom', + SelectPrevUnreadRoom = 'SelectPrevUnreadRoom', /* Select next room with unread messages*/ - NavSelectNextUnreadRoom = 'NavSelectNextUnreadRoom', + SelectNextUnreadRoom = 'SelectNextUnreadRoom', } /** @@ -129,15 +117,15 @@ export type KeyCombo = { shiftKey?: boolean; } -export type KeyBinding = { - action: KeyAction; +export type KeyBinding = { + action: T; keyCombo: KeyCombo; } -const messageComposerBindings = (): KeyBinding[] => { - const bindings: KeyBinding[] = [ +const messageComposerBindings = (): KeyBinding[] => { + const bindings: KeyBinding[] = [ { - action: KeyAction.SelectPrevSendHistory, + action: MessageComposerAction.SelectPrevSendHistory, keyCombo: { key: Key.ARROW_UP, altKey: true, @@ -145,7 +133,7 @@ const messageComposerBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.SelectNextSendHistory, + action: MessageComposerAction.SelectNextSendHistory, keyCombo: { key: Key.ARROW_DOWN, altKey: true, @@ -153,39 +141,39 @@ const messageComposerBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.EditPrevMessage, + action: MessageComposerAction.EditPrevMessage, keyCombo: { key: Key.ARROW_UP, }, }, { - action: KeyAction.EditNextMessage, + action: MessageComposerAction.EditNextMessage, keyCombo: { key: Key.ARROW_DOWN, }, }, { - action: KeyAction.CancelEditing, + action: MessageComposerAction.CancelEditing, keyCombo: { key: Key.ESCAPE, }, }, { - action: KeyAction.FormatBold, + action: MessageComposerAction.FormatBold, keyCombo: { key: Key.B, ctrlOrCmd: true, }, }, { - action: KeyAction.FormatItalics, + action: MessageComposerAction.FormatItalics, keyCombo: { key: Key.I, ctrlOrCmd: true, }, }, { - action: KeyAction.FormatQuote, + action: MessageComposerAction.FormatQuote, keyCombo: { key: Key.GREATER_THAN, ctrlOrCmd: true, @@ -193,7 +181,7 @@ const messageComposerBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.EditUndo, + action: MessageComposerAction.EditUndo, keyCombo: { key: Key.Z, ctrlOrCmd: true, @@ -201,14 +189,14 @@ const messageComposerBindings = (): KeyBinding[] => { }, // Note: the following two bindings also work with just HOME and END, add them here? { - action: KeyAction.MoveCursorToStart, + action: MessageComposerAction.MoveCursorToStart, keyCombo: { key: Key.HOME, ctrlOrCmd: true, }, }, { - action: KeyAction.MoveCursorToEnd, + action: MessageComposerAction.MoveCursorToEnd, keyCombo: { key: Key.END, ctrlOrCmd: true, @@ -217,7 +205,7 @@ const messageComposerBindings = (): KeyBinding[] => { ]; if (isMac) { bindings.push({ - action: KeyAction.EditRedo, + action: MessageComposerAction.EditRedo, keyCombo: { key: Key.Z, ctrlOrCmd: true, @@ -226,7 +214,7 @@ const messageComposerBindings = (): KeyBinding[] => { }); } else { bindings.push({ - action: KeyAction.EditRedo, + action: MessageComposerAction.EditRedo, keyCombo: { key: Key.Y, ctrlOrCmd: true, @@ -235,27 +223,27 @@ const messageComposerBindings = (): KeyBinding[] => { } if (SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend')) { bindings.push({ - action: KeyAction.Send, + action: MessageComposerAction.Send, keyCombo: { key: Key.ENTER, ctrlOrCmd: true, }, }); bindings.push({ - action: KeyAction.NewLine, + action: MessageComposerAction.NewLine, keyCombo: { key: Key.ENTER, }, }); } else { bindings.push({ - action: KeyAction.Send, + action: MessageComposerAction.Send, keyCombo: { key: Key.ENTER, }, }); bindings.push({ - action: KeyAction.NewLine, + action: MessageComposerAction.NewLine, keyCombo: { key: Key.ENTER, shiftKey: true, @@ -263,7 +251,7 @@ const messageComposerBindings = (): KeyBinding[] => { }); if (isMac) { bindings.push({ - action: KeyAction.NewLine, + action: MessageComposerAction.NewLine, keyCombo: { key: Key.ENTER, altKey: true, @@ -274,42 +262,42 @@ const messageComposerBindings = (): KeyBinding[] => { return bindings; } -const autocompleteBindings = (): KeyBinding[] => { +const autocompleteBindings = (): KeyBinding[] => { return [ { - action: KeyAction.AutocompleteApply, + action: AutocompleteAction.ApplySelection, keyCombo: { key: Key.TAB, }, }, { - action: KeyAction.AutocompleteApply, + action: AutocompleteAction.ApplySelection, keyCombo: { key: Key.TAB, ctrlKey: true, }, }, { - action: KeyAction.AutocompleteApply, + action: AutocompleteAction.ApplySelection, keyCombo: { key: Key.TAB, shiftKey: true, }, }, { - action: KeyAction.AutocompleteCancel, + action: AutocompleteAction.Cancel, keyCombo: { key: Key.ESCAPE, }, }, { - action: KeyAction.AutocompletePrevSelection, + action: AutocompleteAction.PrevSelection, keyCombo: { key: Key.ARROW_UP, }, }, { - action: KeyAction.AutocompleteNextSelection, + action: AutocompleteAction.NextSelection, keyCombo: { key: Key.ARROW_DOWN, }, @@ -317,40 +305,40 @@ const autocompleteBindings = (): KeyBinding[] => { ]; } -const roomListBindings = (): KeyBinding[] => { +const roomListBindings = (): KeyBinding[] => { return [ { - action: KeyAction.RoomListClearSearch, + action: RoomListAction.ClearSearch, keyCombo: { key: Key.ESCAPE, }, }, { - action: KeyAction.RoomListPrevRoom, + action: RoomListAction.PrevRoom, keyCombo: { key: Key.ARROW_UP, }, }, { - action: KeyAction.RoomListNextRoom, + action: RoomListAction.NextRoom, keyCombo: { key: Key.ARROW_DOWN, }, }, { - action: KeyAction.RoomListSelectRoom, + action: RoomListAction.SelectRoom, keyCombo: { key: Key.ENTER, }, }, { - action: KeyAction.RoomListCollapseSection, + action: RoomListAction.CollapseSection, keyCombo: { key: Key.ARROW_LEFT, }, }, { - action: KeyAction.RoomListExpandSection, + action: RoomListAction.ExpandSection, keyCombo: { key: Key.ARROW_RIGHT, }, @@ -358,35 +346,35 @@ const roomListBindings = (): KeyBinding[] => { ]; } -const roomBindings = (): KeyBinding[] => { +const roomBindings = (): KeyBinding[] => { const bindings = [ { - action: KeyAction.RoomFocusRoomSearch, + action: RoomAction.FocusRoomSearch, keyCombo: { key: Key.K, ctrlOrCmd: true, }, }, { - action: KeyAction.RoomScrollUp, + action: RoomAction.ScrollUp, keyCombo: { key: Key.PAGE_UP, }, }, { - action: KeyAction.RoomScrollDown, + action: RoomAction.RoomScrollDown, keyCombo: { key: Key.PAGE_DOWN, }, }, { - action: KeyAction.RoomDismissReadMarker, + action: RoomAction.DismissReadMarker, keyCombo: { key: Key.ESCAPE, }, }, { - action: KeyAction.RoomUploadFile, + action: RoomAction.UploadFile, keyCombo: { key: Key.U, ctrlOrCmd: true, @@ -394,14 +382,14 @@ const roomBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.RoomJumpToFirstMessage, + action: RoomAction.JumpToFirstMessage, keyCombo: { key: Key.HOME, ctrlKey: true, }, }, { - action: KeyAction.RoomJumpToLatestMessage, + action: RoomAction.JumpToLatestMessage, keyCombo: { key: Key.END, ctrlKey: true, @@ -411,7 +399,7 @@ const roomBindings = (): KeyBinding[] => { if (SettingsStore.getValue('ctrlFForSearch')) { bindings.push({ - action: KeyAction.RoomSearch, + action: RoomAction.FocusSearch, keyCombo: { key: Key.F, ctrlOrCmd: true, @@ -422,17 +410,17 @@ const roomBindings = (): KeyBinding[] => { return bindings; } -const navigationBindings = (): KeyBinding[] => { +const navigationBindings = (): KeyBinding[] => { return [ { - action: KeyAction.NavToggleRoomSidePanel, + action: NavigationAction.ToggleRoomSidePanel, keyCombo: { key: Key.PERIOD, ctrlOrCmd: true, }, }, { - action: KeyAction.NavToggleUserMenu, + action: NavigationAction.ToggleUserMenu, // Ideally this would be CTRL+P for "Profile", but that's // taken by the print dialog. CTRL+I for "Information" // was previously chosen but conflicted with italics in @@ -443,14 +431,14 @@ const navigationBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.NavToggleShortCutDialog, + action: NavigationAction.ToggleShortCutDialog, keyCombo: { key: Key.SLASH, ctrlOrCmd: true, }, }, { - action: KeyAction.NavToggleShortCutDialog, + action: NavigationAction.ToggleShortCutDialog, keyCombo: { key: Key.SLASH, ctrlOrCmd: true, @@ -458,7 +446,7 @@ const navigationBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.NavGoToHome, + action: NavigationAction.GoToHome, keyCombo: { key: Key.H, ctrlOrCmd: true, @@ -467,21 +455,21 @@ const navigationBindings = (): KeyBinding[] => { }, { - action: KeyAction.NavSelectPrevRoom, + action: NavigationAction.SelectPrevRoom, keyCombo: { key: Key.ARROW_UP, altKey: true, }, }, { - action: KeyAction.NavSelectNextRoom, + action: NavigationAction.SelectNextRoom, keyCombo: { key: Key.ARROW_DOWN, altKey: true, }, }, { - action: KeyAction.NavSelectPrevUnreadRoom, + action: NavigationAction.SelectPrevUnreadRoom, keyCombo: { key: Key.ARROW_UP, altKey: true, @@ -489,7 +477,7 @@ const navigationBindings = (): KeyBinding[] => { }, }, { - action: KeyAction.NavSelectNextUnreadRoom, + action: NavigationAction.SelectNextUnreadRoom, keyCombo: { key: Key.ARROW_DOWN, altKey: true, @@ -551,38 +539,42 @@ export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo: return true; } - -export type KeyBindingsGetter = () => KeyBinding[]; - export class KeyBindingsManager { - /** - * Map of KeyBindingContext to a KeyBinding getter arrow function. - * - * Returning a getter function allowed to have dynamic bindings, e.g. when settings change the bindings can be - * recalculated. - */ - contextBindings: Record = { - [KeyBindingContext.MessageComposer]: messageComposerBindings, - [KeyBindingContext.AutoComplete]: autocompleteBindings, - [KeyBindingContext.RoomList]: roomListBindings, - [KeyBindingContext.Room]: roomBindings, - [KeyBindingContext.Navigation]: navigationBindings, - }; - /** * Finds a matching KeyAction for a given KeyboardEvent */ - getAction(context: KeyBindingContext, ev: KeyboardEvent | React.KeyboardEvent): KeyAction { - const bindings = this.contextBindings[context]?.(); - if (!bindings) { - return KeyAction.None; - } + private getAction(bindings: KeyBinding[], ev: KeyboardEvent | React.KeyboardEvent) + : T | undefined { const binding = bindings.find(it => isKeyComboMatch(ev, it.keyCombo, isMac)); if (binding) { return binding.action; } + return undefined; + } - return KeyAction.None; + getMessageComposerAction(ev: KeyboardEvent | React.KeyboardEvent): MessageComposerAction | undefined { + const bindings = messageComposerBindings(); + return this.getAction(bindings, ev); + } + + getAutocompleteAction(ev: KeyboardEvent | React.KeyboardEvent): AutocompleteAction | undefined { + const bindings = autocompleteBindings(); + return this.getAction(bindings, ev); + } + + getRoomListAction(ev: KeyboardEvent | React.KeyboardEvent): RoomListAction | undefined { + const bindings = roomListBindings(); + return this.getAction(bindings, ev); + } + + getRoomAction(ev: KeyboardEvent | React.KeyboardEvent): RoomAction | undefined { + const bindings = roomBindings(); + return this.getAction(bindings, ev); + } + + getNavigationAction(ev: KeyboardEvent | React.KeyboardEvent): NavigationAction | undefined { + const bindings = navigationBindings(); + return this.getAction(bindings, ev); } } diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index dd8bc1f3db..ce5df47138 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -55,7 +55,7 @@ import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import Modal from "../../Modal"; import { ICollapseConfig } from "../../resizer/distributors/collapse"; import HostSignupContainer from '../views/host_signup/HostSignupContainer'; -import { getKeyBindingsManager, KeyAction, KeyBindingContext } from '../../KeyBindingsManager'; +import { getKeyBindingsManager, NavigationAction, RoomAction } from '../../KeyBindingsManager'; // 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. @@ -401,22 +401,22 @@ class LoggedInView extends React.Component { _onKeyDown = (ev) => { let handled = false; - const roomAction = getKeyBindingsManager().getAction(KeyBindingContext.Room, ev); + const roomAction = getKeyBindingsManager().getRoomAction(ev); switch (roomAction) { - case KeyAction.RoomFocusRoomSearch: + case RoomAction.FocusRoomSearch: dis.dispatch({ action: 'focus_room_filter', }); handled = true; break; - case KeyAction.RoomScrollUp: - case KeyAction.RoomScrollDown: - case KeyAction.RoomJumpToFirstMessage: - case KeyAction.RoomJumpToLatestMessage: + case RoomAction.ScrollUp: + case RoomAction.RoomScrollDown: + case RoomAction.JumpToFirstMessage: + case RoomAction.JumpToLatestMessage: this._onScrollKeyPressed(ev); handled = true; break; - case KeyAction.RoomSearch: + case RoomAction.FocusSearch: dis.dispatch({ action: 'focus_search', }); @@ -429,24 +429,24 @@ class LoggedInView extends React.Component { return; } - const navAction = getKeyBindingsManager().getAction(KeyBindingContext.Navigation, ev); + const navAction = getKeyBindingsManager().getNavigationAction(ev); switch (navAction) { - case KeyAction.NavToggleUserMenu: + case NavigationAction.ToggleUserMenu: dis.fire(Action.ToggleUserMenu); handled = true; break; - case KeyAction.NavToggleShortCutDialog: + case NavigationAction.ToggleShortCutDialog: KeyboardShortcuts.toggleDialog(); handled = true; break; - case KeyAction.NavGoToHome: + case NavigationAction.GoToHome: dis.dispatch({ action: 'view_home_page', }); Modal.closeCurrentModal("homeKeyboardShortcut"); handled = true; break; - case KeyAction.NavToggleRoomSidePanel: + case NavigationAction.ToggleRoomSidePanel: if (this.props.page_type === "room_view" || this.props.page_type === "group_view") { dis.dispatch({ action: Action.ToggleRightPanel, @@ -455,7 +455,7 @@ class LoggedInView extends React.Component { handled = true; } break; - case KeyAction.NavSelectPrevRoom: + case NavigationAction.SelectPrevRoom: dis.dispatch({ action: Action.ViewRoomDelta, delta: -1, @@ -463,7 +463,7 @@ class LoggedInView extends React.Component { }); handled = true; break; - case KeyAction.NavSelectNextRoom: + case NavigationAction.SelectNextRoom: dis.dispatch({ action: Action.ViewRoomDelta, delta: 1, @@ -471,14 +471,14 @@ class LoggedInView extends React.Component { }); handled = true; break; - case KeyAction.NavSelectPrevUnreadRoom: + case NavigationAction.SelectPrevUnreadRoom: dis.dispatch({ action: Action.ViewRoomDelta, delta: -1, unread: true, }); break; - case KeyAction.NavSelectNextUnreadRoom: + case NavigationAction.SelectNextUnreadRoom: dis.dispatch({ action: Action.ViewRoomDelta, delta: 1, diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index 2e900d2f0e..7d127040eb 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -24,7 +24,7 @@ import AccessibleButton from "../views/elements/AccessibleButton"; import { Action } from "../../dispatcher/actions"; import RoomListStore from "../../stores/room-list/RoomListStore"; import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition"; -import { getKeyBindingsManager, KeyAction, KeyBindingContext } from "../../KeyBindingsManager"; +import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager"; interface IProps { isMinimized: boolean; @@ -106,17 +106,17 @@ export default class RoomSearch extends React.PureComponent { }; private onKeyDown = (ev: React.KeyboardEvent) => { - const action = getKeyBindingsManager().getAction(KeyBindingContext.RoomList, ev); + const action = getKeyBindingsManager().getRoomListAction(ev); switch (action) { - case KeyAction.RoomListClearSearch: + case RoomListAction.ClearSearch: this.clearInput(); defaultDispatcher.fire(Action.FocusComposer); break; - case KeyAction.RoomListNextRoom: - case KeyAction.RoomListPrevRoom: + case RoomListAction.NextRoom: + case RoomListAction.PrevRoom: this.props.onVerticalArrow(ev); break; - case KeyAction.RoomListSelectRoom: { + case RoomListAction.SelectRoom: { const shouldClear = this.props.onEnter(ev); if (shouldClear) { // wrap in set immediate to delay it so that we don't clear the filter & then change room diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index c09f1f7c45..680d717615 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -78,7 +78,7 @@ import Notifier from "../../Notifier"; import { showToast as showNotificationsToast } from "../../toasts/DesktopNotificationsToast"; import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore"; import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore"; -import { getKeyBindingsManager, KeyAction, KeyBindingContext } from '../../KeyBindingsManager'; +import { getKeyBindingsManager, RoomAction } from '../../KeyBindingsManager'; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -661,18 +661,18 @@ export default class RoomView extends React.Component { private onReactKeyDown = ev => { let handled = false; - const action = getKeyBindingsManager().getAction(KeyBindingContext.Room, ev); + const action = getKeyBindingsManager().getRoomAction(ev); switch (action) { - case KeyAction.RoomDismissReadMarker: + case RoomAction.DismissReadMarker: this.messagePanel.forgetReadMarker(); this.jumpToLiveTimeline(); handled = true; break; - case KeyAction.RoomScrollUp: + case RoomAction.ScrollUp: this.jumpToReadMarker(); handled = true; break; - case KeyAction.RoomUploadFile: + case RoomAction.UploadFile: dis.dispatch({ action: "upload_file" }, true); handled = true; break; diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index d0119ddc05..f5e561f15a 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -46,7 +46,7 @@ import {IDiff} from "../../../editor/diff"; import AutocompleteWrapperModel from "../../../editor/autocomplete"; import DocumentPosition from "../../../editor/position"; import {ICompletion} from "../../../autocomplete/Autocompleter"; -import { getKeyBindingsManager, KeyBindingContext, KeyAction } from '../../../KeyBindingsManager'; +import { AutocompleteAction, getKeyBindingsManager, MessageComposerAction } from '../../../KeyBindingsManager'; // matches emoticons which follow the start of a line or whitespace const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); @@ -421,21 +421,21 @@ export default class BasicMessageEditor extends React.Component private onKeyDown = (event: React.KeyboardEvent) => { const model = this.props.model; let handled = false; - const action = getKeyBindingsManager().getAction(KeyBindingContext.MessageComposer, event); + const action = getKeyBindingsManager().getMessageComposerAction(event); switch (action) { - case KeyAction.FormatBold: + case MessageComposerAction.FormatBold: this.onFormatAction(Formatting.Bold); handled = true; break; - case KeyAction.FormatItalics: + case MessageComposerAction.FormatItalics: this.onFormatAction(Formatting.Italics); handled = true; break; - case KeyAction.FormatQuote: + case MessageComposerAction.FormatQuote: this.onFormatAction(Formatting.Quote); handled = true; break; - case KeyAction.EditRedo: + case MessageComposerAction.EditRedo: if (this.historyManager.canRedo()) { const {parts, caret} = this.historyManager.redo(); // pass matching inputType so historyManager doesn't push echo @@ -444,7 +444,7 @@ export default class BasicMessageEditor extends React.Component } handled = true; break; - case KeyAction.EditUndo: + case MessageComposerAction.EditUndo: if (this.historyManager.canUndo()) { const {parts, caret} = this.historyManager.undo(this.props.model); // pass matching inputType so historyManager doesn't push echo @@ -453,18 +453,18 @@ export default class BasicMessageEditor extends React.Component } handled = true; break; - case KeyAction.NewLine: + case MessageComposerAction.NewLine: this.insertText("\n"); handled = true; break; - case KeyAction.MoveCursorToStart: + case MessageComposerAction.MoveCursorToStart: setSelection(this.editorRef.current, model, { index: 0, offset: 0, }); handled = true; break; - case KeyAction.MoveCursorToEnd: + case MessageComposerAction.MoveCursorToEnd: setSelection(this.editorRef.current, model, { index: model.parts.length - 1, offset: model.parts[model.parts.length - 1].text.length, @@ -478,30 +478,30 @@ export default class BasicMessageEditor extends React.Component return; } - const autocompleteAction = getKeyBindingsManager().getAction(KeyBindingContext.AutoComplete, event); + const autocompleteAction = getKeyBindingsManager().getAutocompleteAction(event); if (model.autoComplete && model.autoComplete.hasCompletions()) { const autoComplete = model.autoComplete; switch (autocompleteAction) { - case KeyAction.AutocompletePrevSelection: + case AutocompleteAction.PrevSelection: autoComplete.onUpArrow(event); handled = true; break; - case KeyAction.AutocompleteNextSelection: + case AutocompleteAction.NextSelection: autoComplete.onDownArrow(event); handled = true; break; - case KeyAction.AutocompleteApply: + case AutocompleteAction.ApplySelection: autoComplete.onTab(event); handled = true; break; - case KeyAction.AutocompleteCancel: + case AutocompleteAction.Cancel: autoComplete.onEscape(event); handled = true; break; default: return; // don't preventDefault on anything else } - } else if (autocompleteAction === KeyAction.AutocompleteApply) { + } else if (autocompleteAction === AutocompleteAction.ApplySelection) { this.tabCompleteName(event); handled = true; } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index 8aa637f680..1cd2cc7f34 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -32,7 +32,7 @@ import BasicMessageComposer from "./BasicMessageComposer"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {Action} from "../../../dispatcher/actions"; import CountlyAnalytics from "../../../CountlyAnalytics"; -import {getKeyBindingsManager, KeyAction, KeyBindingContext} from '../../../KeyBindingsManager'; +import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager'; function _isReply(mxEvent) { const relatesTo = mxEvent.getContent()["m.relates_to"]; @@ -133,16 +133,16 @@ export default class EditMessageComposer extends React.Component { if (this._editorRef.isComposing(event)) { return; } - const action = getKeyBindingsManager().getAction(KeyBindingContext.MessageComposer, event); + const action = getKeyBindingsManager().getMessageComposerAction(event); switch (action) { - case KeyAction.Send: + case MessageComposerAction.Send: this._sendEdit(); event.preventDefault(); break; - case KeyAction.CancelEditing: + case MessageComposerAction.CancelEditing: this._cancelEdit(); break; - case KeyAction.EditPrevMessage: { + case MessageComposerAction.EditPrevMessage: { if (this._editorRef.isModified() || !this._editorRef.isCaretAtStart()) { return; } @@ -154,7 +154,7 @@ export default class EditMessageComposer extends React.Component { } break; } - case KeyAction.EditNextMessage: { + case MessageComposerAction.EditNextMessage: { if (this._editorRef.isModified() || !this._editorRef.isCaretAtEnd()) { return; } diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index c0919090b0..25e3a34f34 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -51,7 +51,7 @@ import { objectExcluding, objectHasDiff } from "../../../utils/objects"; import TemporaryTile from "./TemporaryTile"; import { ListNotificationState } from "../../../stores/notifications/ListNotificationState"; import IconizedContextMenu from "../context_menus/IconizedContextMenu"; -import { getKeyBindingsManager, KeyAction, KeyBindingContext } from "../../../KeyBindingsManager"; +import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager"; const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS @@ -471,16 +471,16 @@ export default class RoomSublist extends React.Component { }; private onHeaderKeyDown = (ev: React.KeyboardEvent) => { - const action = getKeyBindingsManager().getAction(KeyBindingContext.RoomList, ev); + const action = getKeyBindingsManager().getRoomListAction(ev); switch (action) { - case KeyAction.RoomListCollapseSection: + case RoomListAction.CollapseSection: ev.stopPropagation(); if (this.state.isExpanded) { // Collapse the room sublist if it isn't already this.toggleCollapsed(); } break; - case KeyAction.RoomListExpandSection: { + case RoomListAction.ExpandSection: { ev.stopPropagation(); if (!this.state.isExpanded) { // Expand the room sublist if it isn't already diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index adfa38b56a..b5188b248b 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -46,7 +46,7 @@ import {CHAT_EFFECTS} from '../../../effects'; import CountlyAnalytics from "../../../CountlyAnalytics"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import EMOJI_REGEX from 'emojibase-regex'; -import {getKeyBindingsManager, KeyAction, KeyBindingContext} from '../../../KeyBindingsManager'; +import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager'; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); @@ -143,23 +143,23 @@ export default class SendMessageComposer extends React.Component { if (this._editorRef.isComposing(event)) { return; } - const action = getKeyBindingsManager().getAction(KeyBindingContext.MessageComposer, event); + const action = getKeyBindingsManager().getMessageComposerAction(event); switch (action) { - case KeyAction.Send: + case MessageComposerAction.Send: this._sendMessage(); event.preventDefault(); break; - case KeyAction.SelectPrevSendHistory: - case KeyAction.SelectNextSendHistory: { + case MessageComposerAction.SelectPrevSendHistory: + case MessageComposerAction.SelectNextSendHistory: { // Try select composer history - const selected = this.selectSendHistory(action === KeyAction.SelectPrevSendHistory); + const selected = this.selectSendHistory(action === MessageComposerAction.SelectPrevSendHistory); if (selected) { // We're selecting history, so prevent the key event from doing anything else event.preventDefault(); } break; } - case KeyAction.EditPrevMessage: + case MessageComposerAction.EditPrevMessage: // selection must be collapsed and caret at start if (this._editorRef.isSelectionCollapsed() && this._editorRef.isCaretAtStart()) { const editEvent = findEditableEvent(this.props.room, false); @@ -173,7 +173,7 @@ export default class SendMessageComposer extends React.Component { } } break; - case KeyAction.CancelEditing: + case MessageComposerAction.CancelEditing: dis.dispatch({ action: 'reply_to_event', event: null, From ef7284e69d58fb463c36b50813c46d1348b8cb26 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Mon, 1 Mar 2021 22:15:05 +1300 Subject: [PATCH 161/863] Add missing JumpToOldestUnread action --- src/KeyBindingsManager.ts | 2 ++ src/components/structures/RoomView.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index d8c128a2bf..00e16ce2ab 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -70,6 +70,8 @@ export enum RoomAction { RoomScrollDown = 'RoomScrollDown', /** Dismiss read marker and jump to bottom */ DismissReadMarker = 'DismissReadMarker', + /** Jump to oldest unread message */ + JumpToOldestUnread = 'JumpToOldestUnread', /* Upload a file */ UploadFile = 'UploadFile', /* Focus search message in a room (must be enabled) */ diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 680d717615..9c9dc232a9 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -668,7 +668,7 @@ export default class RoomView extends React.Component { this.jumpToLiveTimeline(); handled = true; break; - case RoomAction.ScrollUp: + case RoomAction.JumpToOldestUnread: this.jumpToReadMarker(); handled = true; break; From 1cfb0e99d43fb5cfe76142c8defa711da0237aed Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Mon, 1 Mar 2021 22:16:05 +1300 Subject: [PATCH 162/863] Add support for multiple key bindings provider - This can be used to provide custom key bindings - Move default key bindings into its own file --- src/KeyBindingsDefaults.ts | 384 ++++++++++++++++++++++++++++++++++ src/KeyBindingsManager.ts | 418 ++++--------------------------------- 2 files changed, 421 insertions(+), 381 deletions(-) create mode 100644 src/KeyBindingsDefaults.ts diff --git a/src/KeyBindingsDefaults.ts b/src/KeyBindingsDefaults.ts new file mode 100644 index 0000000000..ed98a06c7f --- /dev/null +++ b/src/KeyBindingsDefaults.ts @@ -0,0 +1,384 @@ +import { AutocompleteAction, IKeyBindingsProvider, KeyBinding, MessageComposerAction, NavigationAction, RoomAction, + RoomListAction } from "./KeyBindingsManager"; +import { isMac, Key } from "./Keyboard"; +import SettingsStore from "./settings/SettingsStore"; + +const messageComposerBindings = (): KeyBinding[] => { + const bindings: KeyBinding[] = [ + { + action: MessageComposerAction.SelectPrevSendHistory, + keyCombo: { + key: Key.ARROW_UP, + altKey: true, + ctrlKey: true, + }, + }, + { + action: MessageComposerAction.SelectNextSendHistory, + keyCombo: { + key: Key.ARROW_DOWN, + altKey: true, + ctrlKey: true, + }, + }, + { + action: MessageComposerAction.EditPrevMessage, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: MessageComposerAction.EditNextMessage, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + { + action: MessageComposerAction.CancelEditing, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: MessageComposerAction.FormatBold, + keyCombo: { + key: Key.B, + ctrlOrCmd: true, + }, + }, + { + action: MessageComposerAction.FormatItalics, + keyCombo: { + key: Key.I, + ctrlOrCmd: true, + }, + }, + { + action: MessageComposerAction.FormatQuote, + keyCombo: { + key: Key.GREATER_THAN, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: MessageComposerAction.EditUndo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + }, + }, + // Note: the following two bindings also work with just HOME and END, add them here? + { + action: MessageComposerAction.MoveCursorToStart, + keyCombo: { + key: Key.HOME, + ctrlOrCmd: true, + }, + }, + { + action: MessageComposerAction.MoveCursorToEnd, + keyCombo: { + key: Key.END, + ctrlOrCmd: true, + }, + }, + ]; + if (isMac) { + bindings.push({ + action: MessageComposerAction.EditRedo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + shiftKey: true, + }, + }); + } else { + bindings.push({ + action: MessageComposerAction.EditRedo, + keyCombo: { + key: Key.Y, + ctrlOrCmd: true, + }, + }); + } + if (SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend')) { + bindings.push({ + action: MessageComposerAction.Send, + keyCombo: { + key: Key.ENTER, + ctrlOrCmd: true, + }, + }); + bindings.push({ + action: MessageComposerAction.NewLine, + keyCombo: { + key: Key.ENTER, + }, + }); + } else { + bindings.push({ + action: MessageComposerAction.Send, + keyCombo: { + key: Key.ENTER, + }, + }); + bindings.push({ + action: MessageComposerAction.NewLine, + keyCombo: { + key: Key.ENTER, + shiftKey: true, + }, + }); + if (isMac) { + bindings.push({ + action: MessageComposerAction.NewLine, + keyCombo: { + key: Key.ENTER, + altKey: true, + }, + }); + } + } + return bindings; +} + +const autocompleteBindings = (): KeyBinding[] => { + return [ + { + action: AutocompleteAction.ApplySelection, + keyCombo: { + key: Key.TAB, + }, + }, + { + action: AutocompleteAction.ApplySelection, + keyCombo: { + key: Key.TAB, + ctrlKey: true, + }, + }, + { + action: AutocompleteAction.ApplySelection, + keyCombo: { + key: Key.TAB, + shiftKey: true, + }, + }, + { + action: AutocompleteAction.Cancel, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: AutocompleteAction.PrevSelection, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: AutocompleteAction.NextSelection, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + ]; +} + +const roomListBindings = (): KeyBinding[] => { + return [ + { + action: RoomListAction.ClearSearch, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: RoomListAction.PrevRoom, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: RoomListAction.NextRoom, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + { + action: RoomListAction.SelectRoom, + keyCombo: { + key: Key.ENTER, + }, + }, + { + action: RoomListAction.CollapseSection, + keyCombo: { + key: Key.ARROW_LEFT, + }, + }, + { + action: RoomListAction.ExpandSection, + keyCombo: { + key: Key.ARROW_RIGHT, + }, + }, + ]; +} + +const roomBindings = (): KeyBinding[] => { + const bindings = [ + { + action: RoomAction.FocusRoomSearch, + keyCombo: { + key: Key.K, + ctrlOrCmd: true, + }, + }, + { + action: RoomAction.ScrollUp, + keyCombo: { + key: Key.PAGE_UP, + }, + }, + { + action: RoomAction.RoomScrollDown, + keyCombo: { + key: Key.PAGE_DOWN, + }, + }, + { + action: RoomAction.DismissReadMarker, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: RoomAction.JumpToOldestUnread, + keyCombo: { + key: Key.PAGE_UP, + shiftKey: true, + }, + }, + { + action: RoomAction.UploadFile, + keyCombo: { + key: Key.U, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: RoomAction.JumpToFirstMessage, + keyCombo: { + key: Key.HOME, + ctrlKey: true, + }, + }, + { + action: RoomAction.JumpToLatestMessage, + keyCombo: { + key: Key.END, + ctrlKey: true, + }, + }, + ]; + + if (SettingsStore.getValue('ctrlFForSearch')) { + bindings.push({ + action: RoomAction.FocusSearch, + keyCombo: { + key: Key.F, + ctrlOrCmd: true, + }, + }); + } + + return bindings; +} + +const navigationBindings = (): KeyBinding[] => { + return [ + { + action: NavigationAction.ToggleRoomSidePanel, + keyCombo: { + key: Key.PERIOD, + ctrlOrCmd: true, + }, + }, + { + action: NavigationAction.ToggleUserMenu, + // Ideally this would be CTRL+P for "Profile", but that's + // taken by the print dialog. CTRL+I for "Information" + // was previously chosen but conflicted with italics in + // composer, so CTRL+` it is + keyCombo: { + key: Key.BACKTICK, + ctrlOrCmd: true, + }, + }, + { + action: NavigationAction.ToggleShortCutDialog, + keyCombo: { + key: Key.SLASH, + ctrlOrCmd: true, + }, + }, + { + action: NavigationAction.ToggleShortCutDialog, + keyCombo: { + key: Key.SLASH, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: NavigationAction.GoToHome, + keyCombo: { + key: Key.H, + ctrlOrCmd: true, + altKey: true, + }, + }, + + { + action: NavigationAction.SelectPrevRoom, + keyCombo: { + key: Key.ARROW_UP, + altKey: true, + }, + }, + { + action: NavigationAction.SelectNextRoom, + keyCombo: { + key: Key.ARROW_DOWN, + altKey: true, + }, + }, + { + action: NavigationAction.SelectPrevUnreadRoom, + keyCombo: { + key: Key.ARROW_UP, + altKey: true, + shiftKey: true, + }, + }, + { + action: NavigationAction.SelectNextUnreadRoom, + keyCombo: { + key: Key.ARROW_DOWN, + altKey: true, + shiftKey: true, + }, + }, + ] +} + +export const defaultBindingProvider: IKeyBindingsProvider = { + getMessageComposerBindings: messageComposerBindings, + getAutocompleteBindings: autocompleteBindings, + getRoomListBindings: roomListBindings, + getRoomBindings: roomBindings, + getNavigationBindings: navigationBindings, +} diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index 00e16ce2ab..cf11fc711f 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -1,5 +1,5 @@ -import { isMac, Key } from './Keyboard'; -import SettingsStore from './settings/SettingsStore'; +import { defaultBindingProvider } from './KeyBindingsDefaults'; +import { isMac } from './Keyboard'; /** Actions for the chat message composer component */ export enum MessageComposerAction { @@ -124,371 +124,6 @@ export type KeyBinding = { keyCombo: KeyCombo; } -const messageComposerBindings = (): KeyBinding[] => { - const bindings: KeyBinding[] = [ - { - action: MessageComposerAction.SelectPrevSendHistory, - keyCombo: { - key: Key.ARROW_UP, - altKey: true, - ctrlKey: true, - }, - }, - { - action: MessageComposerAction.SelectNextSendHistory, - keyCombo: { - key: Key.ARROW_DOWN, - altKey: true, - ctrlKey: true, - }, - }, - { - action: MessageComposerAction.EditPrevMessage, - keyCombo: { - key: Key.ARROW_UP, - }, - }, - { - action: MessageComposerAction.EditNextMessage, - keyCombo: { - key: Key.ARROW_DOWN, - }, - }, - { - action: MessageComposerAction.CancelEditing, - keyCombo: { - key: Key.ESCAPE, - }, - }, - { - action: MessageComposerAction.FormatBold, - keyCombo: { - key: Key.B, - ctrlOrCmd: true, - }, - }, - { - action: MessageComposerAction.FormatItalics, - keyCombo: { - key: Key.I, - ctrlOrCmd: true, - }, - }, - { - action: MessageComposerAction.FormatQuote, - keyCombo: { - key: Key.GREATER_THAN, - ctrlOrCmd: true, - shiftKey: true, - }, - }, - { - action: MessageComposerAction.EditUndo, - keyCombo: { - key: Key.Z, - ctrlOrCmd: true, - }, - }, - // Note: the following two bindings also work with just HOME and END, add them here? - { - action: MessageComposerAction.MoveCursorToStart, - keyCombo: { - key: Key.HOME, - ctrlOrCmd: true, - }, - }, - { - action: MessageComposerAction.MoveCursorToEnd, - keyCombo: { - key: Key.END, - ctrlOrCmd: true, - }, - }, - ]; - if (isMac) { - bindings.push({ - action: MessageComposerAction.EditRedo, - keyCombo: { - key: Key.Z, - ctrlOrCmd: true, - shiftKey: true, - }, - }); - } else { - bindings.push({ - action: MessageComposerAction.EditRedo, - keyCombo: { - key: Key.Y, - ctrlOrCmd: true, - }, - }); - } - if (SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend')) { - bindings.push({ - action: MessageComposerAction.Send, - keyCombo: { - key: Key.ENTER, - ctrlOrCmd: true, - }, - }); - bindings.push({ - action: MessageComposerAction.NewLine, - keyCombo: { - key: Key.ENTER, - }, - }); - } else { - bindings.push({ - action: MessageComposerAction.Send, - keyCombo: { - key: Key.ENTER, - }, - }); - bindings.push({ - action: MessageComposerAction.NewLine, - keyCombo: { - key: Key.ENTER, - shiftKey: true, - }, - }); - if (isMac) { - bindings.push({ - action: MessageComposerAction.NewLine, - keyCombo: { - key: Key.ENTER, - altKey: true, - }, - }); - } - } - return bindings; -} - -const autocompleteBindings = (): KeyBinding[] => { - return [ - { - action: AutocompleteAction.ApplySelection, - keyCombo: { - key: Key.TAB, - }, - }, - { - action: AutocompleteAction.ApplySelection, - keyCombo: { - key: Key.TAB, - ctrlKey: true, - }, - }, - { - action: AutocompleteAction.ApplySelection, - keyCombo: { - key: Key.TAB, - shiftKey: true, - }, - }, - { - action: AutocompleteAction.Cancel, - keyCombo: { - key: Key.ESCAPE, - }, - }, - { - action: AutocompleteAction.PrevSelection, - keyCombo: { - key: Key.ARROW_UP, - }, - }, - { - action: AutocompleteAction.NextSelection, - keyCombo: { - key: Key.ARROW_DOWN, - }, - }, - ]; -} - -const roomListBindings = (): KeyBinding[] => { - return [ - { - action: RoomListAction.ClearSearch, - keyCombo: { - key: Key.ESCAPE, - }, - }, - { - action: RoomListAction.PrevRoom, - keyCombo: { - key: Key.ARROW_UP, - }, - }, - { - action: RoomListAction.NextRoom, - keyCombo: { - key: Key.ARROW_DOWN, - }, - }, - { - action: RoomListAction.SelectRoom, - keyCombo: { - key: Key.ENTER, - }, - }, - { - action: RoomListAction.CollapseSection, - keyCombo: { - key: Key.ARROW_LEFT, - }, - }, - { - action: RoomListAction.ExpandSection, - keyCombo: { - key: Key.ARROW_RIGHT, - }, - }, - ]; -} - -const roomBindings = (): KeyBinding[] => { - const bindings = [ - { - action: RoomAction.FocusRoomSearch, - keyCombo: { - key: Key.K, - ctrlOrCmd: true, - }, - }, - { - action: RoomAction.ScrollUp, - keyCombo: { - key: Key.PAGE_UP, - }, - }, - { - action: RoomAction.RoomScrollDown, - keyCombo: { - key: Key.PAGE_DOWN, - }, - }, - { - action: RoomAction.DismissReadMarker, - keyCombo: { - key: Key.ESCAPE, - }, - }, - { - action: RoomAction.UploadFile, - keyCombo: { - key: Key.U, - ctrlOrCmd: true, - shiftKey: true, - }, - }, - { - action: RoomAction.JumpToFirstMessage, - keyCombo: { - key: Key.HOME, - ctrlKey: true, - }, - }, - { - action: RoomAction.JumpToLatestMessage, - keyCombo: { - key: Key.END, - ctrlKey: true, - }, - }, - ]; - - if (SettingsStore.getValue('ctrlFForSearch')) { - bindings.push({ - action: RoomAction.FocusSearch, - keyCombo: { - key: Key.F, - ctrlOrCmd: true, - }, - }); - } - - return bindings; -} - -const navigationBindings = (): KeyBinding[] => { - return [ - { - action: NavigationAction.ToggleRoomSidePanel, - keyCombo: { - key: Key.PERIOD, - ctrlOrCmd: true, - }, - }, - { - action: NavigationAction.ToggleUserMenu, - // Ideally this would be CTRL+P for "Profile", but that's - // taken by the print dialog. CTRL+I for "Information" - // was previously chosen but conflicted with italics in - // composer, so CTRL+` it is - keyCombo: { - key: Key.BACKTICK, - ctrlOrCmd: true, - }, - }, - { - action: NavigationAction.ToggleShortCutDialog, - keyCombo: { - key: Key.SLASH, - ctrlOrCmd: true, - }, - }, - { - action: NavigationAction.ToggleShortCutDialog, - keyCombo: { - key: Key.SLASH, - ctrlOrCmd: true, - shiftKey: true, - }, - }, - { - action: NavigationAction.GoToHome, - keyCombo: { - key: Key.H, - ctrlOrCmd: true, - altKey: true, - }, - }, - - { - action: NavigationAction.SelectPrevRoom, - keyCombo: { - key: Key.ARROW_UP, - altKey: true, - }, - }, - { - action: NavigationAction.SelectNextRoom, - keyCombo: { - key: Key.ARROW_DOWN, - altKey: true, - }, - }, - { - action: NavigationAction.SelectPrevUnreadRoom, - keyCombo: { - key: Key.ARROW_UP, - altKey: true, - shiftKey: true, - }, - }, - { - action: NavigationAction.SelectNextUnreadRoom, - keyCombo: { - key: Key.ARROW_DOWN, - altKey: true, - shiftKey: true, - }, - }, - ] -} - /** * Helper method to check if a KeyboardEvent matches a KeyCombo * @@ -541,42 +176,63 @@ export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo: return true; } + +export type KeyBindingGetter = () => KeyBinding[]; + +export interface IKeyBindingsProvider { + getMessageComposerBindings: KeyBindingGetter; + getAutocompleteBindings: KeyBindingGetter; + getRoomListBindings: KeyBindingGetter; + getRoomBindings: KeyBindingGetter; + getNavigationBindings: KeyBindingGetter; +} + export class KeyBindingsManager { + /** + * List of key bindings providers. + * + * Key bindings from the first provider(s) in the list will have precedence over key bindings from later providers. + * + * To overwrite the default key bindings add a new providers before the default provider, e.g. a provider for + * customized key bindings. + */ + bindingsProviders: IKeyBindingsProvider[] = [ + defaultBindingProvider, + ]; + /** * Finds a matching KeyAction for a given KeyboardEvent */ - private getAction(bindings: KeyBinding[], ev: KeyboardEvent | React.KeyboardEvent) + private getAction(getters: KeyBindingGetter[], ev: KeyboardEvent | React.KeyboardEvent) : T | undefined { - const binding = bindings.find(it => isKeyComboMatch(ev, it.keyCombo, isMac)); - if (binding) { - return binding.action; + for (const getter of getters) { + const bindings = getter(); + const binding = bindings.find(it => isKeyComboMatch(ev, it.keyCombo, isMac)); + if (binding) { + return binding.action; + } } return undefined; } getMessageComposerAction(ev: KeyboardEvent | React.KeyboardEvent): MessageComposerAction | undefined { - const bindings = messageComposerBindings(); - return this.getAction(bindings, ev); + return this.getAction(this.bindingsProviders.map(it => it.getMessageComposerBindings), ev); } getAutocompleteAction(ev: KeyboardEvent | React.KeyboardEvent): AutocompleteAction | undefined { - const bindings = autocompleteBindings(); - return this.getAction(bindings, ev); + return this.getAction(this.bindingsProviders.map(it => it.getAutocompleteBindings), ev); } getRoomListAction(ev: KeyboardEvent | React.KeyboardEvent): RoomListAction | undefined { - const bindings = roomListBindings(); - return this.getAction(bindings, ev); + return this.getAction(this.bindingsProviders.map(it => it.getRoomListBindings), ev); } getRoomAction(ev: KeyboardEvent | React.KeyboardEvent): RoomAction | undefined { - const bindings = roomBindings(); - return this.getAction(bindings, ev); + return this.getAction(this.bindingsProviders.map(it => it.getRoomBindings), ev); } getNavigationAction(ev: KeyboardEvent | React.KeyboardEvent): NavigationAction | undefined { - const bindings = navigationBindings(); - return this.getAction(bindings, ev); + return this.getAction(this.bindingsProviders.map(it => it.getNavigationBindings), ev); } } From 9cec3828650490018e674bb4d79ebe97768ce3d4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 27 Feb 2021 22:46:38 -0700 Subject: [PATCH 163/863] Change sending->sent state to match new designs For https://github.com/vector-im/element-web/issues/16424 --- res/css/views/rooms/_EventTile.scss | 39 ++++++++++++------- res/img/element-icons/circle-sending.svg | 3 ++ res/img/element-icons/circle-sent.svg | 4 ++ res/themes/dark/css/_dark.scss | 3 -- res/themes/legacy-dark/css/_legacy-dark.scss | 3 -- .../legacy-light/css/_legacy-light.scss | 2 - res/themes/light/css/_light.scss | 2 - .../views/messages/EditHistoryMessage.js | 1 + src/components/views/rooms/EventTile.js | 21 +++++++++- 9 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 res/img/element-icons/circle-sending.svg create mode 100644 res/img/element-icons/circle-sent.svg diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 5841cf2853..028d9a7556 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -213,23 +213,36 @@ $left-gutter: 64px; color: $accent-fg-color; } -.mx_EventTile_encrypting { - color: $event-encrypting-color !important; -} - -.mx_EventTile_sending { - color: $event-sending-color; -} - -.mx_EventTile_sending .mx_UserPill, -.mx_EventTile_sending .mx_RoomPill { - opacity: 0.5; -} - .mx_EventTile_notSent { color: $event-notsent-color; } +.mx_EventTile_receiptSent, +.mx_EventTile_receiptSending { + // We don't use `position: relative` on the element because then it won't line + // up with the other read receipts + + &::before { + background-color: $tertiary-fg-color; + mask-repeat: no-repeat; + mask-position: center; + mask-size: 14px; + width: 14px; + height: 14px; + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + } +} +.mx_EventTile_receiptSent::before { + mask-image: url('$(res)/img/element-icons/circle-sent.svg'); +} +.mx_EventTile_receiptSending::before { + mask-image: url('$(res)/img/element-icons/circle-sending.svg'); +} + .mx_EventTile_contextual { opacity: 0.4; } diff --git a/res/img/element-icons/circle-sending.svg b/res/img/element-icons/circle-sending.svg new file mode 100644 index 0000000000..2d15a0f716 --- /dev/null +++ b/res/img/element-icons/circle-sending.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/element-icons/circle-sent.svg b/res/img/element-icons/circle-sent.svg new file mode 100644 index 0000000000..04a00ceff7 --- /dev/null +++ b/res/img/element-icons/circle-sent.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index a878aa3cdd..344f012d45 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -137,9 +137,6 @@ $panel-divider-color: transparent; $widget-menu-bar-bg-color: $header-panel-bg-color; $widget-body-bg-color: rgba(141, 151, 165, 0.2); -// event tile lifecycle -$event-sending-color: $text-secondary-color; - // event redaction $event-redacted-fg-color: #606060; $event-redacted-border-color: #000000; diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index 3e3c299af9..ca3ead9ea8 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -132,9 +132,6 @@ $panel-divider-color: $header-panel-border-color; $widget-menu-bar-bg-color: $header-panel-bg-color; $widget-body-bg-color: #1A1D23; -// event tile lifecycle -$event-sending-color: $text-secondary-color; - // event redaction $event-redacted-fg-color: #606060; $event-redacted-border-color: #000000; diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index a740ba155c..fa44c128d0 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -222,8 +222,6 @@ $widget-body-bg-color: #fff; $yellow-background: #fff8e3; // event tile lifecycle -$event-encrypting-color: #abddbc; -$event-sending-color: #ddd; $event-notsent-color: #f44; $event-highlight-fg-color: $warning-color; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 1c89d83c01..ca52d0dcfa 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -222,8 +222,6 @@ $widget-body-bg-color: #FFF; $yellow-background: #fff8e3; // event tile lifecycle -$event-encrypting-color: #abddbc; -$event-sending-color: #ddd; $event-notsent-color: #f44; $event-highlight-fg-color: $warning-color; diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index df27773a40..6c420a16fc 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -158,6 +158,7 @@ export default class EditHistoryMessage extends React.PureComponent { const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.state.sendStatus) !== -1); const classes = classNames({ "mx_EventTile": true, + // Note: we keep these sending state classes for tests, not for our styles "mx_EventTile_sending": isSending, "mx_EventTile_notSent": this.state.sendStatus === 'not_sent', }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 87fb190678..9110316850 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -454,8 +454,26 @@ export default class EventTile extends React.Component { }; getReadAvatars() { - // return early if there are no read receipts + // return early if there are no read receipts, with our message state if applicable if (!this.props.readReceipts || this.props.readReceipts.length === 0) { + const room = this.context.getRoom(this.props.mxEvent.getRoomId()); + const myUserId = MatrixClientPeg.get().getUserId(); + if (this.props.mxEvent.getSender() === myUserId && room) { + // We only search for the most recent 50 events because surely someone will have + // sent *something* in that time, even if it is a membership event or something. + const readUsers = room.getUsersWhoHaveRead(this.props.mxEvent, 50); + const hasBeenRead = readUsers.length === 0 || readUsers.some(u => u !== myUserId); + console.log(room.getUsersReadUpTo(this.props.mxEvent)); + let receipt = null; + if (!this.props.eventSendStatus || this.props.eventSendStatus === 'sent') { + if (!hasBeenRead) { + receipt = ; + } + } else { + receipt = ; + } + return {receipt}; + } return (); } @@ -692,6 +710,7 @@ export default class EventTile extends React.Component { mx_EventTile_isEditing: isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, + // Note: we keep these sending state classes for tests, not for our styles mx_EventTile_encrypting: this.props.eventSendStatus === 'encrypting', mx_EventTile_sending: !isEditing && isSending, mx_EventTile_notSent: this.props.eventSendStatus === 'not_sent', From db8978580c80f86a91dd2783a2415065dc49e57b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 1 Mar 2021 16:21:04 -0700 Subject: [PATCH 164/863] Improve special read receipt checking See comments in code --- src/components/views/rooms/EventTile.js | 133 ++++++++++++++++++++---- 1 file changed, 114 insertions(+), 19 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 9110316850..01e932dd3a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -264,6 +264,79 @@ export default class EventTile extends React.Component { this._tile = createRef(); this._replyThread = createRef(); + + // Throughout the component we manage a read receipt listener to see if our tile still + // qualifies for a "sent" or "sending" state (based on their relevant conditions). We + // don't want to over-subscribe to the read receipt events being fired, so we use a flag + // to determine if we've already subscribed and use a combination of other flags to find + // out if we should even be subscribed at all. + this._isListeningForReceipts = false; + } + + /** + * When true, the tile qualifies for some sort of special read receipt. This could be a 'sending' + * or 'sent' receipt, for example. + * @returns {boolean} + * @private + */ + get _isEligibleForSpecialReceipt() { + // First, if there are other read receipts then just short-circuit this. + if (this.props.readReceipts && this.props.readReceipts.length > 0) return false; + if (!this.props.mxEvent) return false; + + // Sanity check (should never happen, but we shouldn't explode if it does) + const room = this.context.getRoom(this.props.mxEvent.getRoomId()); + if (!room) return false; + + // Quickly check to see if the event was sent by us. If it wasn't, it won't qualify for + // special read receipts. + const myUserId = MatrixClientPeg.get().getUserId(); + if (this.props.mxEvent.getSender() !== myUserId) return false; + + // Finally, determine if the type is relevant to the user. This notably excludes state + // events and pretty much anything that can't be sent by the composer as a message. For + // those we rely on local echo giving the impression of things changing, and expect them + // to be quick. + const simpleSendableEvents = [EventType.Sticker, EventType.RoomMessage, EventType.RoomMessageEncrypted]; + if (!simpleSendableEvents.includes(this.props.mxEvent.getType())) return false; + + // Default case + return true; + } + + get _shouldShowSentReceipt() { + // If we're not even eligible, don't show the receipt. + if (!this._isEligibleForSpecialReceipt) return false; + + // Check to make sure the sending state is appropriate. A null/undefined send status means + // that the message is 'sent', so we're just double checking that it's explicitly not sent. + if (this.props.eventSendStatus && this.props.eventSendStatus !== 'sent') return false; + + // No point in doing the complex math if we're not going to even show this special receipt. + if (this._shouldShowSendingReceipt) return false; + + // Next we check to see if any newer events have read receipts. If they do then we don't + // show our special state - the user already has feedback about their message. We only + // search for the most recent 50 events because surely someone will have sent *something* + // in that time, even if it is a membership event or something. + const room = this.context.getRoom(this.props.mxEvent.getRoomId()); + const myUserId = MatrixClientPeg.get().getUserId(); + const readUsers = room.getUsersWhoHaveRead(this.props.mxEvent, 50); + const hasBeenRead = readUsers.length === 0 || readUsers.some(u => u !== myUserId); + return !hasBeenRead; + } + + get _shouldShowSendingReceipt() { + // If we're not even eligible, don't show the receipt. + if (!this._isEligibleForSpecialReceipt) return false; + + // Check the event send status to see if we are pending. Null/undefined status means the + // message was sent, so check for that and 'sent' explicitly. + if (!this.props.eventSendStatus || this.props.eventSendStatus === 'sent') return false; + + // Default to showing - there's no other event properties/behaviours we care about at + // this point. + return true; } // TODO: [REACT-WARNING] Move into constructor @@ -281,6 +354,11 @@ export default class EventTile extends React.Component { if (this.props.showReactions) { this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated); } + + if (this._shouldShowSentReceipt || this._shouldShowSendingReceipt) { + client.on("Room.receipt", this._onRoomReceipt); + this._isListeningForReceipts = true; + } } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event @@ -305,12 +383,40 @@ export default class EventTile extends React.Component { const client = this.context; client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); + client.removeListener("Room.receipt", this._onRoomReceipt); + this._isListeningForReceipts = false; this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted); if (this.props.showReactions) { this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated); } } + componentDidUpdate(prevProps, prevState, snapshot) { + // If we're not listening for receipts and expect to be, register a listener. + if (!this._isListeningForReceipts && (this._shouldShowSentReceipt || this._shouldShowSendingReceipt)) { + this.context.on("Room.receipt", this._onRoomReceipt); + this._isListeningForReceipts = true; + } + } + + _onRoomReceipt = (ev, room) => { + // ignore events for other rooms + const tileRoom = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + if (room !== tileRoom) return; + + if (!this._shouldShowSentReceipt && !this._shouldShowSendingReceipt && !this._isListeningForReceipts) { + return; + } + + this.forceUpdate(() => { + // Per elsewhere in this file, we can remove the listener once we will have no further purpose for it. + if (!this._shouldShowSentReceipt && !this._shouldShowSendingReceipt) { + this.context.removeListener("Room.receipt", this._onRoomReceipt); + this._isListeningForReceipts = false; + } + }); + }; + /** called when the event is decrypted after we show it. */ _onDecrypted = () => { @@ -454,26 +560,15 @@ export default class EventTile extends React.Component { }; getReadAvatars() { - // return early if there are no read receipts, with our message state if applicable + if (this._shouldShowSentReceipt) { + return ; + } + if (this._shouldShowSendingReceipt) { + return ; + } + + // return early if there are no read receipts if (!this.props.readReceipts || this.props.readReceipts.length === 0) { - const room = this.context.getRoom(this.props.mxEvent.getRoomId()); - const myUserId = MatrixClientPeg.get().getUserId(); - if (this.props.mxEvent.getSender() === myUserId && room) { - // We only search for the most recent 50 events because surely someone will have - // sent *something* in that time, even if it is a membership event or something. - const readUsers = room.getUsersWhoHaveRead(this.props.mxEvent, 50); - const hasBeenRead = readUsers.length === 0 || readUsers.some(u => u !== myUserId); - console.log(room.getUsersReadUpTo(this.props.mxEvent)); - let receipt = null; - if (!this.props.eventSendStatus || this.props.eventSendStatus === 'sent') { - if (!hasBeenRead) { - receipt = ; - } - } else { - receipt = ; - } - return {receipt}; - } return (); } From ebedd3cbcbadaa8d945f430f5934ab7215e6b0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 07:41:14 +0100 Subject: [PATCH 165/863] Remove space Co-authored-by: Travis Ralston --- src/components/views/rooms/AuxPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index b3ef8c3cc8..9d19c212c4 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -154,7 +154,7 @@ export default class AuxPanel extends React.Component { fileDropTarget = (
{ _t("Drop file here to upload") } From ff00683f321a0369bc41836fdc55007c38dfd75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 07:42:07 +0100 Subject: [PATCH 166/863] Use === Co-authored-by: Travis Ralston --- 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 5b79f23e0b..4a58e21820 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1159,7 +1159,7 @@ export default class RoomView extends React.Component { dragCounter: this.state.dragCounter - 1, }); - if (this.state.dragCounter == 0) { + if (this.state.dragCounter === 0) { this.setState({ draggingFile: false, }); From 0a4c0b69b0fa250fa01c3ff1a88ba272dc5ef64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 12:07:33 +0100 Subject: [PATCH 167/863] Move fileDropTarget to RoomView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 17 +++++++++++++++-- src/components/views/rooms/AuxPanel.tsx | 17 ----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 4a58e21820..af7b8ee704 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1782,6 +1782,19 @@ export default class RoomView extends React.Component { } } + let fileDropTarget = null; + if (this.state.draggingFile) { + fileDropTarget = ( +
+ + { _t("Drop file here to upload") } +
+ ); + } + // We have successfully loaded this room, and are not previewing. // Display the "normal" room view. @@ -1893,7 +1906,6 @@ export default class RoomView extends React.Component { room={this.state.room} fullHeight={false} userId={this.context.credentials.userId} - draggingFile={this.state.draggingFile} maxHeight={this.state.auxPanelMaxHeight} showApps={this.state.showApps} onResize={this.onResize} @@ -2059,8 +2071,9 @@ export default class RoomView extends React.Component { />
+ {auxPanel}
- {auxPanel} + {fileDropTarget} {topUnreadMessagesBar} {jumpToBottom} {messagePanel} diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 9d19c212c4..7aa7be42b6 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -35,9 +35,6 @@ interface IProps { userId: string, showApps: boolean, // Render apps - // set to true to show the file drop target - draggingFile: boolean, - // maxHeight attribute for the aux panel and the video // therein maxHeight: number, @@ -149,19 +146,6 @@ export default class AuxPanel extends React.Component { } render() { - let fileDropTarget = null; - if (this.props.draggingFile) { - fileDropTarget = ( -
- - { _t("Drop file here to upload") } -
- ); - } - const callView = ( { { stateViews } { appsDrawer } - { fileDropTarget } { callView } { this.props.children } From 4476843264eeb61380662eb0040a30edfec42c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 12:12:10 +0100 Subject: [PATCH 168/863] Remove unused _t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 7aa7be42b6..c9821d51e3 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -20,7 +20,6 @@ import { Room } from 'matrix-js-sdk/src/models/room' import dis from "../../../dispatcher/dispatcher"; import * as ObjectUtils from '../../../ObjectUtils'; import AppsDrawer from './AppsDrawer'; -import { _t } from '../../../languageHandler'; import classNames from 'classnames'; import RateLimitedFunc from '../../../ratelimitedfunc'; import SettingsStore from "../../../settings/SettingsStore"; From 831cc7eaa0ecd57a25cff40daf582b02e3fd4e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 12:14:36 +0100 Subject: [PATCH 169/863] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- 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 9af8ccc172..5d2c70be03 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1381,7 +1381,6 @@ "Remove %(phone)s?": "Remove %(phone)s?", "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.", "Phone Number": "Phone Number", - "Drop file here to upload": "Drop file here to upload", "This user has not verified all of their sessions.": "This user has not verified all of their sessions.", "You have not verified this user.": "You have not verified this user.", "You have verified this user. This user has verified all of their sessions.": "You have verified this user. This user has verified all of their sessions.", @@ -2513,6 +2512,7 @@ "No more results": "No more results", "Room": "Room", "Failed to reject invite": "Failed to reject invite", + "Drop file here to upload": "Drop file here to upload", "You have %(count)s unread notifications in a prior version of this room.|other": "You have %(count)s unread notifications in a prior version of this room.", "You have %(count)s unread notifications in a prior version of this room.|one": "You have %(count)s unread notification in a prior version of this room.", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", From 4e9d19d3b0690d324e62e379e6e8f90475b55f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 14:09:11 +0100 Subject: [PATCH 170/863] Pass resizeNotifier into CallViewForARoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 1 + src/components/views/voip/CallViewForRoom.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 4ce31be410..21f8369db8 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -169,6 +169,7 @@ export default class AuxPanel extends React.Component { roomId={this.props.room.roomId} onResize={this.props.onResize} maxVideoHeight={this.props.maxHeight} + resizeNotifier={this.props.resizeNotifier} /> ); diff --git a/src/components/views/voip/CallViewForRoom.tsx b/src/components/views/voip/CallViewForRoom.tsx index 4cb4e66fbe..7085cda21a 100644 --- a/src/components/views/voip/CallViewForRoom.tsx +++ b/src/components/views/voip/CallViewForRoom.tsx @@ -19,6 +19,7 @@ import React from 'react'; import CallHandler from '../../../CallHandler'; import CallView from './CallView'; import dis from '../../../dispatcher/dispatcher'; +import ResizeNotifier from "../../../utils/ResizeNotifier"; interface IProps { // What room we should display the call for @@ -30,6 +31,8 @@ interface IProps { // a callback which is called when the content in the callview changes // in a way that is likely to cause a resize. onResize?: any; + + resizeNotifier: ResizeNotifier, } interface IState { From 20e57d15fd2d77e9dc783ef55480a963a5329054 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Mar 2021 15:20:54 +0000 Subject: [PATCH 171/863] Option for audio streaming --- .../views/context_menus/WidgetContextMenu.tsx | 13 +++++++++++++ src/i18n/strings/en_EN.json | 1 + src/stores/widgets/ElementWidgetActions.ts | 1 + 3 files changed, 15 insertions(+) diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index c1af86eae6..e7d1c02c66 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -31,6 +31,7 @@ import QuestionDialog from "../dialogs/QuestionDialog"; import {WidgetType} from "../../../widgets/WidgetType"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; +import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream"; interface IProps extends React.ComponentProps { app: IApp; @@ -54,6 +55,17 @@ const WidgetContextMenu: React.FC = ({ const widgetMessaging = WidgetMessagingStore.instance.getMessagingForId(app.id); const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId); + let streamAudioStreamButton; + if (getConfigLivestreamUrl() && (app.type === "m.jitsi" || app.type === "jitsi")) { + const onStreamAudioClick = () => { + startJitsiAudioLivestream(widgetMessaging, roomId); + onFinished(); + }; + streamAudioStreamButton = ; + } + let unpinButton; if (showUnpin) { const onUnpinClick = () => { @@ -163,6 +175,7 @@ const WidgetContextMenu: React.FC = ({ return + { streamAudioStreamButton } { editButton } { revokeButton } { deleteButton } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 38460a5f6e..7242ed7de6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2357,6 +2357,7 @@ "Set status": "Set status", "Set a new status...": "Set a new status...", "View Community": "View Community", + "Start audio stream": "Start audio stream", "Take a picture": "Take a picture", "Delete Widget": "Delete Widget", "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?", diff --git a/src/stores/widgets/ElementWidgetActions.ts b/src/stores/widgets/ElementWidgetActions.ts index de48746a74..cd591a6fb4 100644 --- a/src/stores/widgets/ElementWidgetActions.ts +++ b/src/stores/widgets/ElementWidgetActions.ts @@ -19,6 +19,7 @@ import { IWidgetApiRequest } from "matrix-widget-api"; export enum ElementWidgetActions { ClientReady = "im.vector.ready", HangupCall = "im.vector.hangup", + StartLiveStream = "im.vector.start_live_stream", OpenIntegrationManager = "integration_manager_open", /** From d1a75885a7a65477ab106e65b745a6e2e972daf6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 Mar 2021 15:35:02 +0000 Subject: [PATCH 172/863] Protect onAction dispatch handler on the SpaceStore with Spaces disabled --- src/stores/SpaceStore.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index d675879138..8e0066da91 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -408,6 +408,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onAction(payload: ActionPayload) { + if (!SettingsStore.getValue("feature_spaces")) return; switch (payload.action) { case "view_room": { const room = this.matrixClient?.getRoom(payload.room_id); From 08d35073de93367b059b23c1581b8eb765822754 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 2 Mar 2021 11:04:12 -0700 Subject: [PATCH 173/863] Improve commentary --- src/components/views/messages/EditHistoryMessage.js | 2 +- src/components/views/rooms/EventTile.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 6c420a16fc..0967be937a 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -158,7 +158,7 @@ export default class EditHistoryMessage extends React.PureComponent { const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.state.sendStatus) !== -1); const classes = classNames({ "mx_EventTile": true, - // Note: we keep these sending state classes for tests, not for our styles + // Note: we keep the `sending` state class for tests, not for our styles "mx_EventTile_sending": isSending, "mx_EventTile_notSent": this.state.sendStatus === 'not_sent', }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 01e932dd3a..b4192fc8d3 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -408,6 +408,8 @@ export default class EventTile extends React.Component { return; } + // We force update because we have no state or prop changes to queue up, instead relying on + // the getters we use here to determine what needs rendering. this.forceUpdate(() => { // Per elsewhere in this file, we can remove the listener once we will have no further purpose for it. if (!this._shouldShowSentReceipt && !this._shouldShowSendingReceipt) { @@ -805,8 +807,7 @@ export default class EventTile extends React.Component { mx_EventTile_isEditing: isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, - // Note: we keep these sending state classes for tests, not for our styles - mx_EventTile_encrypting: this.props.eventSendStatus === 'encrypting', + // Note: we keep the `sending` state class for tests, not for our styles mx_EventTile_sending: !isEditing && isSending, mx_EventTile_notSent: this.props.eventSendStatus === 'not_sent', mx_EventTile_highlight: this.props.tileShape === 'notif' ? false : this.shouldHighlight(), From 69cdbef3d6e07f690a15626523fdaae1ad551a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Mar 2021 20:31:39 +0100 Subject: [PATCH 174/863] Remove maxHeight prop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/voip/CallView.tsx | 18 ++---------------- src/components/views/voip/VideoFeed.tsx | 8 +------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/components/views/voip/CallView.tsx b/src/components/views/voip/CallView.tsx index 7cac682794..6e7a9a635d 100644 --- a/src/components/views/voip/CallView.tsx +++ b/src/components/views/voip/CallView.tsx @@ -39,9 +39,6 @@ interface IProps { // Another ongoing call to display information about secondaryCall?: MatrixCall, - // maxHeight style attribute for the video panel - maxVideoHeight?: number; - // a callback which is called when the content in the callview changes // in a way that is likely to cause a resize. onResize?: any; @@ -517,20 +514,9 @@ export default class CallView extends React.Component { localVideoFeed = ; } - // if we're fullscreen, we don't want to set a maxHeight on the video element. - const maxVideoHeight = getFullScreenElement() || !this.props.maxVideoHeight ? null : ( - this.props.maxVideoHeight - (HEADER_HEIGHT + BOTTOM_PADDING + BOTTOM_MARGIN_TOP_BOTTOM) - ); - contentView =
+ contentView =
{onHoldBackground} - + {localVideoFeed} {onHoldContent} {callControls} diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index 5210f28eb1..1e950f3a2a 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -29,9 +29,6 @@ interface IProps { type: VideoFeedType, - // maxHeight style attribute for the video element - maxHeight?: number, - // a callback which is called when the video element is resized // due to a change in video metadata onResize?: (e: Event) => void, @@ -80,9 +77,6 @@ export default class VideoFeed extends React.Component { ), }; - let videoStyle = {}; - if (this.props.maxHeight) videoStyle = { maxHeight: this.props.maxHeight }; - - return
From 0e5297fcb008147cd0c80e7b5fcf9bb1b95dc0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 3 Mar 2021 16:44:43 +0100 Subject: [PATCH 183/863] Fix local feed size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/voip/_VideoFeed.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/views/voip/_VideoFeed.scss b/res/css/views/voip/_VideoFeed.scss index ed5bb86420..8ead8bba3e 100644 --- a/res/css/views/voip/_VideoFeed.scss +++ b/res/css/views/voip/_VideoFeed.scss @@ -22,7 +22,8 @@ limitations under the License. } .mx_VideoFeed_local { - width: 20%; + width: 25%; + height: 25%; position: absolute; right: 10px; top: 10px; From c638ced21e53c3334d9f7d0b7919e6f8133818c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 3 Mar 2021 16:59:39 +0100 Subject: [PATCH 184/863] Remove unused consts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/voip/CallView.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/voip/CallView.tsx b/src/components/views/voip/CallView.tsx index 6e7a9a635d..b4dad3b19a 100644 --- a/src/components/views/voip/CallView.tsx +++ b/src/components/views/voip/CallView.tsx @@ -92,9 +92,6 @@ function exitFullscreen() { const CONTROLS_HIDE_DELAY = 1000; // Height of the header duplicated from CSS because we need to subtract it from our max // height to get the max height of the video -const HEADER_HEIGHT = 44; -const BOTTOM_PADDING = 10; -const BOTTOM_MARGIN_TOP_BOTTOM = 10; // top margin plus bottom margin const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px) export default class CallView extends React.Component { From ae08f74336d3aa24988f7f3dba7d817cd5c96741 Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Wed, 3 Mar 2021 22:38:30 +0200 Subject: [PATCH 185/863] feat: improve "view source" display encrypted and decrypted event source on the same dialog keep only one "View Source" action on event context menu --- res/css/structures/_ViewSource.scss | 11 ++++++- src/components/structures/ViewSource.js | 32 ++++++++++++++----- .../views/context_menus/MessageContextMenu.js | 12 ++----- .../views/dialogs/RoomSettingsDialog.js | 2 +- .../views/dialogs/UserSettingsDialog.js | 2 +- src/i18n/strings/en_EN.json | 4 ++- src/i18n/strings/en_US.json | 4 ++- 7 files changed, 44 insertions(+), 23 deletions(-) diff --git a/res/css/structures/_ViewSource.scss b/res/css/structures/_ViewSource.scss index 421d1f03cd..0cb5dd8f3a 100644 --- a/res/css/structures/_ViewSource.scss +++ b/res/css/structures/_ViewSource.scss @@ -22,9 +22,18 @@ limitations under the License. float: right; } -.mx_ViewSource_label_bottom { +.mx_ViewSource_separator { clear: both; border-bottom: 1px solid #e5e5e5; + padding-top: 0.7em; + padding-bottom: 0.7em; +} + +.mx_ViewSource_heading { + font-size: $font-17px; + font-weight: 400; + color: $primary-fg-color; + margin-top: 0.7em; } .mx_ViewSource pre { diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js index 0b969784e5..866f2e0a0b 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.js @@ -19,7 +19,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import SyntaxHighlight from '../views/elements/SyntaxHighlight'; -import {_t} from "../../languageHandler"; +import {_t, _td} from "../../languageHandler"; import * as sdk from "../../index"; @@ -29,20 +29,36 @@ export default class ViewSource extends React.Component { onFinished: PropTypes.func.isRequired, roomId: PropTypes.string.isRequired, eventId: PropTypes.string.isRequired, + isEncrypted: PropTypes.bool.isRequired, + decryptedContent: PropTypes.object, }; render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + + const DecryptedSection = <> +
+
{_t("Decrypted event source")}
+ + { JSON.stringify(this.props.decryptedContent, null, 2) } + + ; + + const OriginalSection = <> +
+
{_t("Original event source")}
+ + { JSON.stringify(this.props.content, null, 2) } + + ; + return ( -
Room ID: { this.props.roomId }
-
Event ID: { this.props.eventId }
-
-
- - { JSON.stringify(this.props.content, null, 2) } - +
Room ID: { this.props.roomId }
+
Event ID: { this.props.eventId }
+ { this.props.isEncrypted && DecryptedSection } + { OriginalSection }
); diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index 6b871e4f24..b002d1ec62 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -130,6 +130,8 @@ export default class MessageContextMenu extends React.Component { roomId: ev.getRoomId(), eventId: ev.getId(), content: ev.event, + isEncrypted: this.props.mxEvent.getType() !== this.props.mxEvent.getWireType(), + decryptedContent: ev._clearEvent, }, 'mx_Dialog_viewsource'); this.closeMenu(); }; @@ -309,7 +311,6 @@ export default class MessageContextMenu extends React.Component { let cancelButton; let forwardButton; let pinButton; - let viewClearSourceButton; let unhidePreviewButton; let externalURLButton; let quoteButton; @@ -389,14 +390,6 @@ export default class MessageContextMenu extends React.Component { ); - if (mxEvent.getType() !== mxEvent.getWireType()) { - viewClearSourceButton = ( - - { _t('View Decrypted Source') } - - ); - } - if (this.props.eventTileOps) { if (this.props.eventTileOps.isWidgetHidden()) { unhidePreviewButton = ( @@ -481,7 +474,6 @@ export default class MessageContextMenu extends React.Component { { forwardButton } { pinButton } { viewSourceButton } - { viewClearSourceButton } { unhidePreviewButton } { permalinkButton } { quoteButton } diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 9d9313f08f..368f2aeccd 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -116,7 +116,7 @@ export default class RoomSettingsDialog extends React.Component { return ( -
+
diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 7164540aea..3291fa2387 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -155,7 +155,7 @@ export default class UserSettingsDialog extends React.Component { return ( -
+
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fa7f446b7c..b777adcf0c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2875,5 +2875,7 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "Decrypted event source": "Decrypted event source", + "Original event source": "Original event source" } diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index a1275fb089..9dc6d18f8a 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -650,5 +650,7 @@ "Error upgrading room": "Error upgrading room", "Double check that your server supports the room version chosen and try again.": "Double check that your server supports the room version chosen and try again.", "Changes the avatar of the current room": "Changes the avatar of the current room", - "Changes your avatar in all rooms": "Changes your avatar in all rooms" + "Changes your avatar in all rooms": "Changes your avatar in all rooms", + "Decrypted event source": "Decrypted event source", + "Original event source": "Original event source" } From 0a1f372371a73736a720deface3564be172a9953 Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Wed, 3 Mar 2021 23:26:31 +0200 Subject: [PATCH 186/863] fix: lint --- src/components/structures/ViewSource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js index 866f2e0a0b..b57efe1dd3 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.js @@ -19,7 +19,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import SyntaxHighlight from '../views/elements/SyntaxHighlight'; -import {_t, _td} from "../../languageHandler"; +import {_t} from "../../languageHandler"; import * as sdk from "../../index"; From 6d792cc08cec87e5ad6a856c5e4c9e593ac974df Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Wed, 3 Mar 2021 23:48:39 +0200 Subject: [PATCH 187/863] feat: use
to hide encrypted block --- res/css/structures/_ViewSource.scss | 4 +++ src/components/structures/ViewSource.js | 47 ++++++++++++++++--------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/res/css/structures/_ViewSource.scss b/res/css/structures/_ViewSource.scss index 0cb5dd8f3a..0126c16599 100644 --- a/res/css/structures/_ViewSource.scss +++ b/res/css/structures/_ViewSource.scss @@ -43,3 +43,7 @@ limitations under the License. word-wrap: break-word; white-space: pre-wrap; } + +.mx_ViewSource_details { + margin-top: 0.8em; +} diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js index b57efe1dd3..ca6c0d4226 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.js @@ -36,29 +36,42 @@ export default class ViewSource extends React.Component { render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DecryptedSection = <> -
-
{_t("Decrypted event source")}
- - { JSON.stringify(this.props.decryptedContent, null, 2) } - - ; - - const OriginalSection = <> -
-
{_t("Original event source")}
- - { JSON.stringify(this.props.content, null, 2) } - - ; + let content; + if (this.props.isEncrypted) { + content = <> +
+ + {_t("Decrypted event source")} + + + { JSON.stringify(this.props.decryptedContent, null, 2) } + +
+
+ + {_t("Original event source")} + + + { JSON.stringify(this.props.content, null, 2) } + +
+ ; + } else { + content = <> +
{_t("Original event source")}
+ + { JSON.stringify(this.props.content, null, 2) } + + ; + } return (
Room ID: { this.props.roomId }
Event ID: { this.props.eventId }
- { this.props.isEncrypted && DecryptedSection } - { OriginalSection } +
+ { content }
); From 725162ee0012c85111859fa54c9058462945e761 Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Thu, 4 Mar 2021 00:09:00 +0200 Subject: [PATCH 188/863] fix: i18n strings --- src/i18n/strings/en_EN.json | 7 +++---- src/i18n/strings/en_US.json | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b777adcf0c..c3752d7942 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2396,7 +2396,6 @@ "Cancel Sending": "Cancel Sending", "Forward Message": "Forward Message", "Pin Message": "Pin Message", - "View Decrypted Source": "View Decrypted Source", "Unhide Preview": "Unhide Preview", "Share Permalink": "Share Permalink", "Share Message": "Share Message", @@ -2652,6 +2651,8 @@ "User menu": "User menu", "Community and user menu": "Community and user menu", "Could not load user profile": "Could not load user profile", + "Decrypted event source": "Decrypted event source", + "Original event source": "Original event source", "Verify this login": "Verify this login", "Session verified": "Session verified", "Failed to send email": "Failed to send email", @@ -2875,7 +2876,5 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End", - "Decrypted event source": "Decrypted event source", - "Original event source": "Original event source" + "End": "End" } diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 9dc6d18f8a..a1275fb089 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -650,7 +650,5 @@ "Error upgrading room": "Error upgrading room", "Double check that your server supports the room version chosen and try again.": "Double check that your server supports the room version chosen and try again.", "Changes the avatar of the current room": "Changes the avatar of the current room", - "Changes your avatar in all rooms": "Changes your avatar in all rooms", - "Decrypted event source": "Decrypted event source", - "Original event source": "Original event source" + "Changes your avatar in all rooms": "Changes your avatar in all rooms" } From 1cb19554eb3a151706b6b964f0c42f61b58c7a23 Mon Sep 17 00:00:00 2001 From: Ayush Kumar <2580ayush2580@gmail.com> Date: Thu, 4 Mar 2021 14:03:02 +0530 Subject: [PATCH 189/863] Fix Bottom border of state counters is white on the dark theme --- res/css/views/rooms/_AuxPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_AuxPanel.scss b/res/css/views/rooms/_AuxPanel.scss index 34ef5e01d4..17a6294bf0 100644 --- a/res/css/views/rooms/_AuxPanel.scss +++ b/res/css/views/rooms/_AuxPanel.scss @@ -17,7 +17,7 @@ limitations under the License. .m_RoomView_auxPanel_stateViews { padding: 5px; padding-left: 19px; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; } .m_RoomView_auxPanel_stateViews_span a { From 63944b9f6da84dc4b385cb3f7f3b995cabab56e4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Mar 2021 12:22:31 +0000 Subject: [PATCH 190/863] Add the new file --- src/Livestream.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/Livestream.ts diff --git a/src/Livestream.ts b/src/Livestream.ts new file mode 100644 index 0000000000..d4bed63dbd --- /dev/null +++ b/src/Livestream.ts @@ -0,0 +1,52 @@ +/* +Copyright 2021 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 { ClientWidgetApi } from "matrix-widget-api"; +import { MatrixClientPeg } from "./MatrixClientPeg"; +import SdkConfig from "./SdkConfig"; +import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions"; + +export function getConfigLivestreamUrl() { + return SdkConfig.get()["audioStreamUrl"]; +} + +async function createLiveStream(roomId: string) { + const openIdToken = await MatrixClientPeg.get().getOpenIdToken(); + + const url = getConfigLivestreamUrl() + "/createStream"; + + const response = await window.fetch(url, { + method: 'POST', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + room_id: roomId, + openid_token: openIdToken, + }), + }); + + const respBody = response.json(); + return respBody['stream_id']; +} + +export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string) { + const streamId = await createLiveStream(roomId); + + widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, { + rtmpStreamKey: 'audioStream:' + streamId, + }); +} From ab4220b20d98d4e47574f945baee64bbf2ec3b48 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Mar 2021 13:04:58 +0000 Subject: [PATCH 191/863] Defer auto-joining within spaces and switch to using `suggested` --- .../structures/SpaceRoomDirectory.tsx | 33 ++++++++++--------- src/i18n/strings/en_EN.json | 2 +- src/stores/SpaceStore.tsx | 3 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/components/structures/SpaceRoomDirectory.tsx b/src/components/structures/SpaceRoomDirectory.tsx index 7f7b9dbb99..0a59ac0195 100644 --- a/src/components/structures/SpaceRoomDirectory.tsx +++ b/src/components/structures/SpaceRoomDirectory.tsx @@ -64,6 +64,7 @@ export interface ISpaceSummaryEvent { state_key: string; content: { order?: string; + suggested?: boolean; auto_join?: boolean; via?: string; }; @@ -91,7 +92,7 @@ const SubSpace: React.FC = ({ const name = space.name || space.canonical_alias || space.aliases?.[0] || _t("Unnamed Space"); const evContent = event?.getContent(); - const [autoJoin, _setAutoJoin] = useState(evContent?.auto_join); + const [suggested, _setSuggested] = useState(evContent?.suggested); const [removed, _setRemoved] = useState(!evContent?.via); const cli = MatrixClientPeg.get(); @@ -102,12 +103,12 @@ const SubSpace: React.FC = ({ let actions; if (editing && queueAction) { if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) { - const setAutoJoin = () => { - _setAutoJoin(v => { + const setSuggested = () => { + _setSuggested(v => { queueAction({ event, removed, - autoJoin: !v, + suggested: !v, }); return !v; }); @@ -118,7 +119,7 @@ const SubSpace: React.FC = ({ queueAction({ event, removed: !v, - autoJoin, + suggested, }); return !v; }); @@ -131,7 +132,7 @@ const SubSpace: React.FC = ({ } else { actions = - + ; } } else { @@ -180,8 +181,8 @@ const SubSpace: React.FC = ({ interface IAction { event: MatrixEvent; + suggested: boolean; removed: boolean; - autoJoin: boolean; } interface IRoomTileProps { @@ -197,7 +198,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli const name = room.name || room.canonical_alias || room.aliases?.[0] || _t("Unnamed Room"); const evContent = event?.getContent(); - const [autoJoin, _setAutoJoin] = useState(evContent?.auto_join); + const [suggested, _setSuggested] = useState(evContent?.suggested); const [removed, _setRemoved] = useState(!evContent?.via); const cli = MatrixClientPeg.get(); @@ -207,12 +208,12 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli let actions; if (editing && queueAction) { if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) { - const setAutoJoin = () => { - _setAutoJoin(v => { + const setSuggested = () => { + _setSuggested(v => { queueAction({ event, removed, - autoJoin: !v, + suggested: !v, }); return !v; }); @@ -223,7 +224,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli queueAction({ event, removed: !v, - autoJoin, + suggested, }); return !v; }); @@ -236,7 +237,7 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli } else { actions = - + ; } } else { @@ -441,10 +442,10 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis const onSaveButtonClicked = () => { // TODO setBusy - pendingActions.current.forEach(({event, autoJoin, removed}) => { + pendingActions.current.forEach(({event, suggested, removed}) => { const content = { ...event.getContent(), - auto_join: autoJoin, + suggested, }; if (removed) { @@ -459,7 +460,7 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis if (isEditing) { adminButton = - { _t("All users join by default") } + { _t("Promoted to users") } ; } else { adminButton = ; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e256e6bb2f..7b680b6590 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2596,7 +2596,7 @@ "You're in this space": "You're in this space", "You're in this room": "You're in this room", "Save changes": "Save changes", - "All users join by default": "All users join by default", + "Promoted to users": "Promoted to users", "Manage rooms": "Manage rooms", "Find a room...": "Find a room...", "Accept Invite": "Accept Invite", diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 8e0066da91..c334144d70 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -108,9 +108,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } } - public addRoomToSpace(space: Room, roomId: string, via: string[], autoJoin = false) { + public addRoomToSpace(space: Room, roomId: string, via: string[], suggested = false, autoJoin = false) { return this.matrixClient.sendStateEvent(space.roomId, EventType.SpaceChild, { via, + suggested, auto_join: autoJoin, }, roomId); } From dd792c3d7329400bf67c95cf671190203d54afd7 Mon Sep 17 00:00:00 2001 From: Ayush Kumar <2580ayush2580@gmail.com> Date: Thu, 4 Mar 2021 19:24:35 +0530 Subject: [PATCH 192/863] Fix Clicking on the avatar for opening member info requires pixel-perfect accuracy --- res/css/views/rooms/_GroupLayout.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 903fabc8fd..818509785b 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -21,7 +21,7 @@ $left-gutter: 64px; .mx_EventTile { > .mx_SenderProfile { line-height: $font-20px; - padding-left: $left-gutter; + margin-left: $left-gutter; } > .mx_EventTile_line { From 0f1b7a001e1b943ff28acc664b009264cc7fd588 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Mar 2021 17:52:49 +0000 Subject: [PATCH 193/863] Better error handling for streams Also use older youtubeStreamKey as it appears our jitsi doesn't support the newer one. --- src/Livestream.ts | 6 +++--- .../views/context_menus/WidgetContextMenu.tsx | 15 +++++++++++++-- src/i18n/strings/en_EN.json | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Livestream.ts b/src/Livestream.ts index d4bed63dbd..cd8cdea179 100644 --- a/src/Livestream.ts +++ b/src/Livestream.ts @@ -39,14 +39,14 @@ async function createLiveStream(roomId: string) { }), }); - const respBody = response.json(); + const respBody = await response.json(); return respBody['stream_id']; } export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string) { const streamId = await createLiveStream(roomId); - widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, { - rtmpStreamKey: 'audioStream:' + streamId, + await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, { + rtmpStreamKey: 'rtmp://audiostream.dummy/' + streamId, }); } diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index e7d1c02c66..0503df038a 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -28,6 +28,7 @@ import dis from "../../../dispatcher/dispatcher"; import SettingsStore from "../../../settings/SettingsStore"; import Modal from "../../../Modal"; import QuestionDialog from "../dialogs/QuestionDialog"; +import ErrorDialog from "../dialogs/ErrorDialog"; import {WidgetType} from "../../../widgets/WidgetType"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; @@ -57,8 +58,18 @@ const WidgetContextMenu: React.FC = ({ let streamAudioStreamButton; if (getConfigLivestreamUrl() && (app.type === "m.jitsi" || app.type === "jitsi")) { - const onStreamAudioClick = () => { - startJitsiAudioLivestream(widgetMessaging, roomId); + const onStreamAudioClick = async () => { + try { + await startJitsiAudioLivestream(widgetMessaging, roomId); + } catch (err) { + console.log("Failed to start livestream", err); + // XXX: won't i18n well, but looks like widget api only support 'message'? + const message = err.message || _t("Unable to start audio streaming."); + Modal.createTrackedDialog('WidgetContext Menu', 'Livestream failed', ErrorDialog, { + title: _t('Failed to start livestream'), + description: message, + }); + } onFinished(); }; streamAudioStreamButton = Date: Thu, 4 Mar 2021 17:58:43 +0000 Subject: [PATCH 194/863] 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 f33c4368ef..2183a0c68f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2357,8 +2357,8 @@ "Set status": "Set status", "Set a new status...": "Set a new status...", "View Community": "View Community", - "Failed to start livestream": "Failed to start livestream", "Unable to start audio streaming.": "Unable to start audio streaming.", + "Failed to start livestream": "Failed to start livestream", "Start audio stream": "Start audio stream", "Take a picture": "Take a picture", "Delete Widget": "Delete Widget", From af5cfff51db48c4a96c5393a25c76675839e864d Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Thu, 4 Mar 2021 23:17:29 +0200 Subject: [PATCH 195/863] feat: edit button on View Source dialog reuse component SendCustomEvent swap it in place in the View Source dialog the Back button takes you to the View Source dialog, not the DevTools dialog do not display the flip toggle box for changing between State Event and Normal Event --- src/components/structures/ViewSource.js | 180 ++++++++++++++---- .../views/context_menus/MessageContextMenu.js | 14 +- .../views/dialogs/DevtoolsDialog.js | 7 +- 3 files changed, 152 insertions(+), 49 deletions(-) diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js index ca6c0d4226..7fe862cff5 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.js @@ -16,12 +16,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; -import SyntaxHighlight from '../views/elements/SyntaxHighlight'; -import {_t} from "../../languageHandler"; +import React from "react"; +import PropTypes from "prop-types"; +import SyntaxHighlight from "../views/elements/SyntaxHighlight"; +import { _t } from "../../languageHandler"; import * as sdk from "../../index"; - +import MatrixClientContext from "../../contexts/MatrixClientContext"; +import { SendCustomEvent } from "../views/dialogs/DevtoolsDialog"; export default class ViewSource extends React.Component { static propTypes = { @@ -31,48 +32,157 @@ export default class ViewSource extends React.Component { eventId: PropTypes.string.isRequired, isEncrypted: PropTypes.bool.isRequired, decryptedContent: PropTypes.object, + event: PropTypes.object.isRequired, // the MatrixEvent associated with the context menu }; + constructor(props) { + super(props); + + this.state = { + editComponent: null, + }; + } + + onBack() { + this.setState({ editComponent: null }); + } + + editEvent() { + const isStateEvent = this.props.event.isState(); + console.log("isStateEvent", isStateEvent); + if (isStateEvent) { + this.setState({ + editComponent: ( + + {(cli) => ( + this.onBack()} + inputs={{ + eventType: this.props.event.getType(), + evContent: JSON.stringify( + this.props.event.getContent(), + null, + "\t" + ), + stateKey: this.props.event.getStateKey(), + }} + /> + )} + + ), + }); + } else { + // send an edit-message event + // prefill the "m.new_content" field + const originalContent = this.props.event.getContent(); + const originalEventId = this.props.eventId; + const content = { + ...originalContent, + "m.new_content": originalContent, + "m.relates_to": { + rel_type: "m.replace", + event_id: originalEventId, + }, + }; + this.setState({ + editComponent: ( + + {(cli) => ( + this.onBack()} + inputs={{ + eventType: this.props.event.getType(), + evContent: JSON.stringify( + content, + null, + "\t" + ), + }} + /> + )} + + ), + }); + } + } + render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog"); let content; if (this.props.isEncrypted) { - content = <> -
- - {_t("Decrypted event source")} - - - { JSON.stringify(this.props.decryptedContent, null, 2) } - -
-
- - {_t("Original event source")} - - - { JSON.stringify(this.props.content, null, 2) } - -
- ; + content = ( + <> +
+ + + {_t("Decrypted event source")} + + + + {JSON.stringify( + this.props.decryptedContent, + null, + 2 + )} + +
+
+ + + {_t("Original event source")} + + + + {JSON.stringify(this.props.content, null, 2)} + +
+ + ); } else { - content = <> -
{_t("Original event source")}
- - { JSON.stringify(this.props.content, null, 2) } - - ; + content = ( + <> +
+ {_t("Original event source")} +
+ + {JSON.stringify(this.props.content, null, 2)} + + + ); } + const isEditing = this.state.editComponent !== null; + console.log(isEditing); + return ( - -
-
Room ID: { this.props.roomId }
-
Event ID: { this.props.eventId }
+ +
+
+ Room ID: {this.props.roomId} +
+
+ Event ID: {this.props.eventId} +
- { content } + {isEditing ? this.state.editComponent : content}
+ {!isEditing && ( +
+ +
+ )} ); } diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index b002d1ec62..a1c111b19c 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -130,20 +130,10 @@ export default class MessageContextMenu extends React.Component { roomId: ev.getRoomId(), eventId: ev.getId(), content: ev.event, + event: ev, isEncrypted: this.props.mxEvent.getType() !== this.props.mxEvent.getWireType(), - decryptedContent: ev._clearEvent, - }, 'mx_Dialog_viewsource'); - this.closeMenu(); - }; - - onViewClearSourceClick = () => { - const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent; - const ViewSource = sdk.getComponent('structures.ViewSource'); - Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, { - roomId: ev.getRoomId(), - eventId: ev.getId(), // FIXME: _clearEvent is private - content: ev._clearEvent, + decryptedContent: ev._clearEvent, }, 'mx_Dialog_viewsource'); this.closeMenu(); }; diff --git a/src/components/views/dialogs/DevtoolsDialog.js b/src/components/views/dialogs/DevtoolsDialog.js index 814378bb51..5d571461fc 100644 --- a/src/components/views/dialogs/DevtoolsDialog.js +++ b/src/components/views/dialogs/DevtoolsDialog.js @@ -73,13 +73,14 @@ class GenericEditor extends React.PureComponent { } } -class SendCustomEvent extends GenericEditor { +export class SendCustomEvent extends GenericEditor { static getLabel() { return _t('Send Custom Event'); } static propTypes = { onBack: PropTypes.func.isRequired, room: PropTypes.instanceOf(Room).isRequired, forceStateEvent: PropTypes.bool, + forceGeneralEvent: PropTypes.bool, inputs: PropTypes.object, }; @@ -140,6 +141,8 @@ class SendCustomEvent extends GenericEditor {
; } + const showTglFlip = !this.state.message && !this.props.forceStateEvent && !this.props.forceGeneralEvent; + return
@@ -155,7 +158,7 @@ class SendCustomEvent extends GenericEditor {
{ !this.state.message && } - { !this.state.message && !this.props.forceStateEvent &&
+ { showTglFlip &&
} From 288d98daede9f1ea6b1045e6de930c61f5f14c0e Mon Sep 17 00:00:00 2001 From: Panagiotis <27917356+panoschal@users.noreply.github.com> Date: Fri, 5 Mar 2021 00:07:59 +0200 Subject: [PATCH 196/863] chore: format, lint --- src/components/structures/ViewSource.js | 58 ++++--------------- .../views/dialogs/DevtoolsDialog.js | 2 +- 2 files changed, 13 insertions(+), 47 deletions(-) diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js index 7fe862cff5..a31876ea76 100644 --- a/src/components/structures/ViewSource.js +++ b/src/components/structures/ViewSource.js @@ -61,11 +61,7 @@ export default class ViewSource extends React.Component { onBack={() => this.onBack()} inputs={{ eventType: this.props.event.getType(), - evContent: JSON.stringify( - this.props.event.getContent(), - null, - "\t" - ), + evContent: JSON.stringify(this.props.event.getContent(), null, "\t"), stateKey: this.props.event.getStateKey(), }} /> @@ -97,11 +93,7 @@ export default class ViewSource extends React.Component { onBack={() => this.onBack()} inputs={{ eventType: this.props.event.getType(), - evContent: JSON.stringify( - content, - null, - "\t" - ), + evContent: JSON.stringify(content, null, "\t"), }} /> )} @@ -120,39 +112,23 @@ export default class ViewSource extends React.Component { <>
- - {_t("Decrypted event source")} - + {_t("Decrypted event source")} - - {JSON.stringify( - this.props.decryptedContent, - null, - 2 - )} - + {JSON.stringify(this.props.decryptedContent, null, 2)}
- - {_t("Original event source")} - + {_t("Original event source")} - - {JSON.stringify(this.props.content, null, 2)} - + {JSON.stringify(this.props.content, null, 2)}
); } else { content = ( <> -
- {_t("Original event source")} -
- - {JSON.stringify(this.props.content, null, 2)} - +
{_t("Original event source")}
+ {JSON.stringify(this.props.content, null, 2)} ); } @@ -161,26 +137,16 @@ export default class ViewSource extends React.Component { console.log(isEditing); return ( - +
-
- Room ID: {this.props.roomId} -
-
- Event ID: {this.props.eventId} -
+
Room ID: {this.props.roomId}
+
Event ID: {this.props.eventId}
{isEditing ? this.state.editComponent : content}
{!isEditing && (
- +
)} diff --git a/src/components/views/dialogs/DevtoolsDialog.js b/src/components/views/dialogs/DevtoolsDialog.js index 5d571461fc..82f2df6534 100644 --- a/src/components/views/dialogs/DevtoolsDialog.js +++ b/src/components/views/dialogs/DevtoolsDialog.js @@ -142,7 +142,7 @@ export class SendCustomEvent extends GenericEditor { } const showTglFlip = !this.state.message && !this.props.forceStateEvent && !this.props.forceGeneralEvent; - + return
From 5d6e3d5711b56eddb764e0ee8924b20c4e5ed5e2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Mar 2021 20:07:48 -0700 Subject: [PATCH 197/863] UI refresh for uploaded files Fixes https://github.com/vector-im/element-web/issues/16557 Fixes https://github.com/vector-im/element-web/issues/9482 (technically) There's two changes in this: 1. The actual file body in the timeline now has a placeholder thing. 2. We're intentionally dropping all the "Travis uploaded a file" sender profile states. --- res/css/views/messages/_MFileBody.scss | 43 +++++++++++++++++++ res/themes/dark/css/_dark.scss | 4 ++ res/themes/legacy-dark/css/_legacy-dark.scss | 4 ++ .../legacy-light/css/_legacy-light.scss | 4 ++ res/themes/light/css/_light.scss | 4 ++ src/components/views/messages/MAudioBody.js | 2 +- src/components/views/messages/MFileBody.js | 26 ++++++++++- src/components/views/messages/MImageBody.js | 2 +- src/components/views/messages/MVideoBody.tsx | 2 +- .../views/messages/SenderProfile.js | 10 +---- src/components/views/rooms/EventTile.js | 7 +-- src/i18n/strings/en_EN.json | 3 -- 12 files changed, 88 insertions(+), 23 deletions(-) diff --git a/res/css/views/messages/_MFileBody.scss b/res/css/views/messages/_MFileBody.scss index 6cbce68745..e219c0c5e4 100644 --- a/res/css/views/messages/_MFileBody.scss +++ b/res/css/views/messages/_MFileBody.scss @@ -45,3 +45,46 @@ limitations under the License. * big the content of the iframe is. */ height: 1.5em; } + +.mx_MFileBody_info { + background-color: $message-body-panel-bg-color; + border-radius: 4px; + width: 270px; + padding: 8px; + color: $message-body-panel-fg-color; + + .mx_MFileBody_info_icon { + background-color: $message-body-panel-icon-bg-color; + border-radius: 20px; + display: inline-block; + width: 32px; + height: 32px; + position: relative; + vertical-align: middle; + margin-right: 12px; + + &::before { + content: ''; + mask-repeat: no-repeat; + mask-position: center; + mask-size: cover; + mask-image: url('$(res)/img/element-icons/room/composer/attach.svg'); + background-color: $message-body-panel-fg-color; + width: 13px; + height: 15px; + + position: absolute; + top: 8px; + left: 9px; + } + } + + .mx_MFileBody_info_filename { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: inline-block; + width: calc(100% - 32px - 12px); // 32px icon, 12px margin on the icon + vertical-align: middle; + } +} diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 0de5e69782..a4648d2051 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -203,6 +203,10 @@ $breadcrumb-placeholder-bg-color: #272c35; $user-tile-hover-bg-color: $header-panel-bg-color; +$message-body-panel-bg-color: #21262c82; +$message-body-panel-icon-bg-color: #8e99a4; +$message-body-panel-fg-color: $primary-fg-color; + // Appearance tab colors $appearance-tab-border-color: $room-highlight-color; diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index 8c5f20178b..8752e41d18 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -198,6 +198,10 @@ $breadcrumb-placeholder-bg-color: #272c35; $user-tile-hover-bg-color: $header-panel-bg-color; +$message-body-panel-bg-color: #21262c82; +$message-body-panel-icon-bg-color: #8e99a4; +$message-body-panel-fg-color: $primary-fg-color; + // Appearance tab colors $appearance-tab-border-color: $room-highlight-color; diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 3ba10a68ea..58b75f32aa 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -322,6 +322,10 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; +$message-body-panel-bg-color: #e3e8f082; +$message-body-panel-icon-bg-color: #ffffff; +$message-body-panel-fg-color: $muted-fg-color; + // FontSlider colors $appearance-tab-border-color: $input-darker-bg-color; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 76bf2ddc21..fd2bfe4628 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -323,6 +323,10 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; +$message-body-panel-bg-color: #e3e8f082; +$message-body-panel-icon-bg-color: #ffffff; +$message-body-panel-fg-color: $muted-fg-color; + // FontSlider colors $appearance-tab-border-color: $input-darker-bg-color; diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index 37f85a108f..587dee4513 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -105,7 +105,7 @@ export default class MAudioBody extends React.Component { return ( ); } diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 770cd4fff3..676f0b7986 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -126,6 +126,12 @@ export default class MFileBody extends React.Component { onHeightChanged: PropTypes.func, /* the shape of the tile, used */ tileShape: PropTypes.string, + /* whether or not to show the default placeholder for the file. Defaults to true. */ + showGenericPlaceholder: PropTypes.bool, + }; + + static defaultProps = { + showGenericPlaceholder: true, }; constructor(props) { @@ -145,9 +151,10 @@ export default class MFileBody extends React.Component { * link text. * * @param {Object} content The "content" key of the matrix event. + * @param {boolean} withSize Whether to include size information. Default true. * @return {string} the human readable link text for the attachment. */ - presentableTextForFile(content) { + presentableTextForFile(content, withSize = true) { let linkText = _t("Attachment"); if (content.body && content.body.length > 0) { // The content body should be the name of the file including a @@ -155,7 +162,7 @@ export default class MFileBody extends React.Component { linkText = content.body; } - if (content.info && content.info.size) { + if (content.info && content.info.size && withSize) { // If we know the size of the file then add it as human readable // string to the end of the link text so that the user knows how // big a file they are downloading. @@ -218,6 +225,16 @@ export default class MFileBody extends React.Component { const fileSize = content.info ? content.info.size : null; const fileType = content.info ? content.info.mimetype : "application/octet-stream"; + let placeholder = null; + if (this.props.showGenericPlaceholder) { + placeholder = ( +
+ + {this.presentableTextForFile(content, false)} +
+ ); + } + if (isEncrypted) { if (this.state.decryptedBlob === null) { // Need to decrypt the attachment @@ -248,6 +265,7 @@ export default class MFileBody extends React.Component { // but it is not guaranteed between various browsers' settings. return ( + {placeholder}
{ _t("Decrypt %(text)s", { text: text }) } @@ -278,6 +296,7 @@ export default class MFileBody extends React.Component { // If the attachment is encrypted then put the link inside an iframe. return ( + {placeholder}
{ /* @@ -346,6 +365,7 @@ export default class MFileBody extends React.Component { if (this.props.tileShape === "file_grid") { return ( + {placeholder}
{ fileName } @@ -359,6 +379,7 @@ export default class MFileBody extends React.Component { } else { return ( + {placeholder}
@@ -371,6 +392,7 @@ export default class MFileBody extends React.Component { } else { const extra = text ? (': ' + text) : ''; return + {placeholder} { _t("Invalid file%(extra)s", { extra: extra }) } ; } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index a8cdc17abf..771d12accd 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -452,7 +452,7 @@ export default class MImageBody extends React.Component { // Overidden by MStickerBody getFileBody() { - return ; + return ; } render() { diff --git a/src/components/views/messages/MVideoBody.tsx b/src/components/views/messages/MVideoBody.tsx index 9628f11809..ce4a4eda76 100644 --- a/src/components/views/messages/MVideoBody.tsx +++ b/src/components/views/messages/MVideoBody.tsx @@ -243,7 +243,7 @@ export default class MVideoBody extends React.PureComponent { onPlay={this.videoOnPlay} > - + ); } diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index afe2d6d118..e2bb1ff38d 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -25,7 +25,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; export default class SenderProfile extends React.Component { static propTypes = { mxEvent: PropTypes.object.isRequired, // event whose sender we're showing - text: PropTypes.string, // Text to show. Defaults to sender name onClick: PropTypes.func, }; @@ -118,17 +117,10 @@ export default class SenderProfile extends React.Component { { flair } ; - const content = this.props.text ? - - - { _t(this.props.text, { senderName: () => nameElem }) } - - : nameFlair; - return (
- { content } + { nameFlair }
); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 87fb190678..2009eb6d1c 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -768,15 +768,10 @@ export default class EventTile extends React.Component { } if (needsSenderProfile) { - let text = null; if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') { - 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 = ; + enableFlair={this.props.enableFlair} />; } else { sender = ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a1999acb3b..f3232416b1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1430,9 +1430,6 @@ "Edit message": "Edit message", "Mod": "Mod", "This event could not be displayed": "This event could not be displayed", - "%(senderName)s sent an image": "%(senderName)s sent an image", - "%(senderName)s sent a video": "%(senderName)s sent a video", - "%(senderName)s uploaded a file": "%(senderName)s uploaded a file", "Your key share request has been sent - please check your other sessions for key share requests.": "Your key share request has been sent - please check your other sessions for key share requests.", "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.": "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.", "If your other sessions do not have the key for this message you will not be able to decrypt them.": "If your other sessions do not have the key for this message you will not be able to decrypt them.", From 8d143331a8d8fa72c89b8730f70b9b3ff9ba7009 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Mar 2021 20:10:47 -0700 Subject: [PATCH 198/863] Appease the linter --- src/components/views/messages/SenderProfile.js | 1 - src/components/views/rooms/EventTile.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index e2bb1ff38d..d2db05252c 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -18,7 +18,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import Flair from '../elements/Flair.js'; import FlairStore from '../../../stores/FlairStore'; -import { _t } from '../../../languageHandler'; import {getUserNameColorClass} from '../../../utils/FormattingUtils'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 2009eb6d1c..198b3427bc 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -22,7 +22,7 @@ import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; import {EventType} from "matrix-js-sdk/src/@types/event"; -import { _t, _td } from '../../../languageHandler'; +import { _t } from '../../../languageHandler'; import * as TextForEvent from "../../../TextForEvent"; import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; From d7310bc5b36cb66abbee507ff74cea3f56592c70 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Mar 2021 20:17:29 -0700 Subject: [PATCH 199/863] Remove dead classes --- res/css/views/rooms/_IRCLayout.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 792c2f1f58..21baa795e6 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -181,8 +181,7 @@ $irc-line-height: $font-18px; > span { display: flex; - > .mx_SenderProfile_name, - > .mx_SenderProfile_aux { + > .mx_SenderProfile_name { overflow: hidden; text-overflow: ellipsis; min-width: var(--name-width); @@ -212,8 +211,7 @@ $irc-line-height: $font-18px; background: transparent; > span { - > .mx_SenderProfile_name, - > .mx_SenderProfile_aux { + > .mx_SenderProfile_name { min-width: inherit; } } From dadeb68bbfceade031150be13f3bfdb6e9ae0f6d Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Fri, 5 Mar 2021 22:02:18 +1300 Subject: [PATCH 200/863] Fix spelling --- src/KeyBindingsDefaults.ts | 2 +- src/KeyBindingsManager.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KeyBindingsDefaults.ts b/src/KeyBindingsDefaults.ts index f777f2c5f6..847867ae4f 100644 --- a/src/KeyBindingsDefaults.ts +++ b/src/KeyBindingsDefaults.ts @@ -382,7 +382,7 @@ const navigationBindings = (): KeyBinding[] => { ] } -export const defaultBindingProvider: IKeyBindingsProvider = { +export const defaultBindingsProvider: IKeyBindingsProvider = { getMessageComposerBindings: messageComposerBindings, getAutocompleteBindings: autocompleteBindings, getRoomListBindings: roomListBindings, diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index cf11fc711f..725bfd65f1 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -1,4 +1,4 @@ -import { defaultBindingProvider } from './KeyBindingsDefaults'; +import { defaultBindingsProvider } from './KeyBindingsDefaults'; import { isMac } from './Keyboard'; /** Actions for the chat message composer component */ @@ -197,7 +197,7 @@ export class KeyBindingsManager { * customized key bindings. */ bindingsProviders: IKeyBindingsProvider[] = [ - defaultBindingProvider, + defaultBindingsProvider, ]; /** From efc5d413c48162687a8e21688f628646ccdb49a4 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Fri, 5 Mar 2021 22:13:47 +1300 Subject: [PATCH 201/863] Fix missing import (from earlier merge conflict) --- src/components/views/rooms/SendMessageComposer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 0c0495fe20..1902498914 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -47,6 +47,7 @@ import CountlyAnalytics from "../../../CountlyAnalytics"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import EMOJI_REGEX from 'emojibase-regex'; import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager'; +import SettingsStore from '../../../settings/SettingsStore'; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); From c80cbc38dd28e89d1b643189acb9c659914989dc Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Mar 2021 10:32:54 +0000 Subject: [PATCH 202/863] Use helper class (It did not need imports) Co-authored-by: Travis Ralston --- src/components/views/context_menus/WidgetContextMenu.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index 0503df038a..03e63edbcc 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -57,7 +57,7 @@ const WidgetContextMenu: React.FC = ({ const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId); let streamAudioStreamButton; - if (getConfigLivestreamUrl() && (app.type === "m.jitsi" || app.type === "jitsi")) { + if (getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) { const onStreamAudioClick = async () => { try { await startJitsiAudioLivestream(widgetMessaging, roomId); @@ -199,4 +199,3 @@ const WidgetContextMenu: React.FC = ({ }; export default WidgetContextMenu; - From 8bcf0f08385368ce0dce69510e814f478aa90e2c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Mar 2021 10:34:03 +0000 Subject: [PATCH 203/863] console.error Co-authored-by: Travis Ralston --- src/components/views/context_menus/WidgetContextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index 03e63edbcc..623fe04f2f 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -62,7 +62,7 @@ const WidgetContextMenu: React.FC = ({ try { await startJitsiAudioLivestream(widgetMessaging, roomId); } catch (err) { - console.log("Failed to start livestream", err); + console.error("Failed to start livestream", err); // XXX: won't i18n well, but looks like widget api only support 'message'? const message = err.message || _t("Unable to start audio streaming."); Modal.createTrackedDialog('WidgetContext Menu', 'Livestream failed', ErrorDialog, { From 572f15522f28320f671980a5892ec2cf10fd0d8e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Mar 2021 10:40:11 +0000 Subject: [PATCH 204/863] use a constant --- src/Livestream.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Livestream.ts b/src/Livestream.ts index cd8cdea179..2389132762 100644 --- a/src/Livestream.ts +++ b/src/Livestream.ts @@ -23,6 +23,9 @@ export function getConfigLivestreamUrl() { return SdkConfig.get()["audioStreamUrl"]; } +// Dummy rtmp URL used to signal that we want a special audio-only stream +const AUDIOSTREAM_DUMMY_URL = 'rtmp://audiostream.dummy/'; + async function createLiveStream(roomId: string) { const openIdToken = await MatrixClientPeg.get().getOpenIdToken(); @@ -47,6 +50,6 @@ export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi const streamId = await createLiveStream(roomId); await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, { - rtmpStreamKey: 'rtmp://audiostream.dummy/' + streamId, + rtmpStreamKey: AUDIOSTREAM_DUMMY_URL + streamId, }); } From 1c1d239d5b0853e2b6ee56f823020655d2a68a2b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 5 Mar 2021 13:19:06 +0000 Subject: [PATCH 205/863] Add Edge to the targets list Part of https://github.com/vector-im/element-web/issues/9175 --- babel.config.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/babel.config.js b/babel.config.js index d5a97d56ce..0a3a34a391 100644 --- a/babel.config.js +++ b/babel.config.js @@ -3,12 +3,15 @@ module.exports = { "presets": [ ["@babel/preset-env", { "targets": [ - "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" + "last 2 Chrome versions", + "last 2 Firefox versions", + "last 2 Safari versions", + "last 2 Edge versions", ], }], "@babel/preset-typescript", "@babel/preset-flow", - "@babel/preset-react" + "@babel/preset-react", ], "plugins": [ ["@babel/plugin-proposal-decorators", {legacy: true}], @@ -18,6 +21,6 @@ module.exports = { "@babel/plugin-proposal-object-rest-spread", "@babel/plugin-transform-flow-comments", "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-transform-runtime" - ] + "@babel/plugin-transform-runtime", + ], }; From 35dbc82b87f5aba8b5ab91afa4a7afb6d0ab5a4a Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 5 Mar 2021 15:01:48 +0000 Subject: [PATCH 206/863] Add issue comments --- src/components/structures/LoggedInView.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 508b7f05e7..5acc8c6891 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -93,6 +93,8 @@ interface IProps { } interface IUsageLimit { + // "hs_disabled" is NOT a specced string, but is used in Synapse + // This is tracked over at https://github.com/matrix-org/synapse/issues/9237 // eslint-disable-next-line camelcase limit_type: "monthly_active_user" | "hs_disabled" | string; // eslint-disable-next-line camelcase @@ -102,6 +104,8 @@ interface IUsageLimit { interface IState { syncErrorData?: { error: { + // This is not specced, but used in Synapse. See + // https://github.com/matrix-org/synapse/issues/9237#issuecomment-768238922 data: IUsageLimit; errcode: string; }; From e5b03488d890a3c289cd06d0e2ab8dd11bbb77ce Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 Mar 2021 17:52:51 +0000 Subject: [PATCH 207/863] Fix widget resizing There was a line of CSS that set pointer-events: none on widget iframes whilst they were being resized to stop iframes swallowing the mousemove/up events while dragging the resize handle, but a) all widgets are now in a persisted element wrapper and therefore not in the right place in the DOM to get that CSS and b) that only got set when resizing the whole aps drawer vertically, not dragging the handle between apps to change the width distribution. Add a pointer events prop to AppTile to allow the pointer-events style to be set by the parent, and set it when dragging either resize handle. Fixes https://github.com/vector-im/element-web/issues/16473 --- res/css/views/rooms/_AppsDrawer.scss | 5 ----- src/components/views/elements/AppTile.js | 14 ++++++++++---- src/components/views/rooms/AppsDrawer.js | 14 +++++++++++++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 492ed95973..fd80836237 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -370,11 +370,6 @@ $MinWidth: 240px; display: none; } -/* Avoid apptile iframes capturing mouse event focus when resizing */ -.mx_AppsDrawer_resizing iframe { - pointer-events: none; -} - .mx_AppsDrawer_resizing .mx_AppTile_persistedWrapper { z-index: 1; } diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 213351889f..747d00a8df 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -328,6 +328,10 @@ export default class AppTile extends React.Component { const iframeFeatures = "microphone; camera; encrypted-media; autoplay; display-capture;"; const appTileBodyClass = 'mx_AppTileBody' + (this.props.miniMode ? '_mini ' : ' '); + const appTileBodyStyles = {}; + if (this.props.pointerEvents) { + appTileBodyStyles['pointer-events'] = this.props.pointerEvents; + } const loadingElement = (
@@ -338,7 +342,7 @@ export default class AppTile extends React.Component { // only possible for room widgets, can assert this.props.room here const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); appTileBody = ( -
+
+
{ loadingElement }
); } else { if (this.isMixedContent()) { appTileBody = ( -
+
); } else { appTileBody = ( -
+
{ this.state.loading && loadingElement }