diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 5fc7a5f04b..c587251d3e 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -501,10 +501,12 @@ $left-gutter: 64px; } } -.mx_EventTile_content_collapsedCode { - pre { - max-height: 30vh; - } +.mx_EventTile_expandedCodeBlock { + max-height: 100vh; +} + +.mx_EventTile_collapsedCodeBlock { + max-height: 30vh; } .mx_EventTile:hover .mx_EventTile_body pre, @@ -531,6 +533,35 @@ $left-gutter: 64px; background-color: $message-action-bar-fg-color; } + +// Inserted adjacent to
blocks, (See TextualBody)
+.mx_EventTile_expandButton {
+ position: absolute;
+ display: inline-block;
+ visibility: hidden;
+ cursor: pointer;
+ top: 6px;
+ right: 6px;
+ width: 19px;
+ height: 19px;
+ mask-image: url($copy-button-url);
+ background-color: $message-action-bar-fg-color;
+}
+
+// Inserted adjacent to blocks, (See TextualBody)
+.mx_EventTile_collapseButton {
+ position: absolute;
+ display: inline-block;
+ visibility: hidden;
+ cursor: pointer;
+ top: 6px;
+ right: 6px;
+ width: 19px;
+ height: 19px;
+ mask-image: url($copy-button-url);
+ background-color: $message-action-bar-fg-color;
+}
+
.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
visibility: visible;
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index ff864d9c13..8d341e4a63 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -35,7 +35,6 @@ import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
import {toRightOf} from "../../structures/ContextMenu";
import {copyPlaintext} from "../../../utils/strings";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
-import classNames from "classnames";
export default class TextualBody extends React.Component {
static propTypes = {
@@ -70,7 +69,6 @@ export default class TextualBody extends React.Component {
// track whether the preview widget is hidden
widgetHidden: false,
- codeBlockExpanded: SettingsStore.getValue("expandCodeByDefault"),
};
}
@@ -93,29 +91,70 @@ export default class TextualBody extends React.Component {
this.calculateUrlPreview();
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
- const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("code");
+ const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
if (blocks.length > 0) {
+ for (let i = 0; i < blocks.length; i++) {
+ this._handleCodeBlockExpansion(blocks[i]);
+ this._addCodeCopyButton(blocks[i]);
+ }
// Do this asynchronously: parsing code takes time and we don't
// need to block the DOM update on it.
setTimeout(() => {
if (this._unmounted) return;
for (let i = 0; i < blocks.length; i++) {
- if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
- highlight.highlightBlock(blocks[i]);
- } else {
- // Only syntax highlight if there's a class starting with language-
- const classes = blocks[i].className.split(/\s+/).filter(function(cl) {
- return cl.startsWith('language-') && !cl.startsWith('language-_');
- });
-
- if (classes.length != 0) {
- highlight.highlightBlock(blocks[i]);
- }
- }
+ this._highlightCode(blocks[i].firstChild);
}
}, 10);
}
- this._addCodeCopyButton();
+ }
+ }
+
+ _addCodeCopyButton(codeBlock) {
+ const button = document.createElement("span");
+ button.className = "mx_EventTile_copyButton";
+ button.onclick = async () => {
+ const copyCode = button.parentNode.getElementsByTagName("pre")[0];
+ const successful = await copyPlaintext(copyCode.textContent);
+
+ const buttonRect = button.getBoundingClientRect();
+ const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
+ const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
+ ...toRightOf(buttonRect, 2),
+ message: successful ? _t('Copied!') : _t('Failed to copy'),
+ });
+ button.onmouseleave = close;
+ };
+
+ // Wrap a div around so that the copy button can be correctly positioned
+ // when the overflows and is scrolled horizontally.
+ const div = document.createElement("div");
+ div.className = "mx_EventTile_pre_container";
+
+ // Insert containing div in place of block
+ codeBlock.parentNode.replaceChild(div, codeBlock);
+
+ // Append block and copy button to container
+ div.appendChild(codeBlock);
+ div.appendChild(button);
+ }
+
+ _handleCodeBlockExpansion(codeBlock) {
+ const expandCodeBlock = SettingsStore.getValue("expandCodeByDefault");
+ codeBlock.className = expandCodeBlock ? "mx_EventTile_expandedCodeBlock" : "mx_EventTile_collapsedCodeBlock";
+ }
+
+ _highlightCode(codeBlock) {
+ if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
+ highlight.highlightBlock(codeBlock);
+ } else {
+ // Only syntax highlight if there's a class starting with language-
+ const classes = codeBlock.className.split(/\s+/).filter(function(cl) {
+ return cl.startsWith('language-') && !cl.startsWith('language-_');
+ });
+
+ if (classes.length != 0) {
+ highlight.highlightBlock(codeBlock);
+ }
}
}
@@ -256,38 +295,6 @@ export default class TextualBody extends React.Component {
}
}
- _addCodeCopyButton() {
- // Add 'copy' buttons to pre blocks
- Array.from(ReactDOM.findDOMNode(this).querySelectorAll('.mx_EventTile_body pre')).forEach((p) => {
- const button = document.createElement("span");
- button.className = "mx_EventTile_copyButton";
- button.onclick = async () => {
- const copyCode = button.parentNode.getElementsByTagName("pre")[0];
- const successful = await copyPlaintext(copyCode.textContent);
-
- const buttonRect = button.getBoundingClientRect();
- const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
- const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
- ...toRightOf(buttonRect, 2),
- message: successful ? _t('Copied!') : _t('Failed to copy'),
- });
- button.onmouseleave = close;
- };
-
- // Wrap a div around so that the copy button can be correctly positioned
- // when the overflows and is scrolled horizontally.
- const div = document.createElement("div");
- div.className = "mx_EventTile_pre_container";
-
- // Insert containing div in place of block
- p.parentNode.replaceChild(div, p);
-
- // Append block and copy button to container
- div.appendChild(p);
- div.appendChild(button);
- });
- }
-
onCancelClick = event => {
this.setState({ widgetHidden: true });
// FIXME: persist this somewhere smarter than local storage
@@ -439,12 +446,6 @@ export default class TextualBody extends React.Component {
});
}
- const defaultCaseClasses = classNames({
- mx_MTextBody: true,
- mx_EventTile_content: true,
- mx_EventTile_content_collapsedCode: !this.state.codeBlockExpanded,
- });
-
switch (content.msgtype) {
case "m.emote":
return (
@@ -470,7 +471,7 @@ export default class TextualBody extends React.Component {
);
default: // including "m.text"
return (
-
+
{ body }
{ widgets }