Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add full Markdown support, syntax highlighting, collapsible spoilers and more. #2470

Open
wants to merge 9 commits into
base: clearnet
Choose a base branch
from
2 changes: 2 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@
"noteToSelf": "Note to Self",
"hideMenuBarTitle": "Hide Menu Bar",
"hideMenuBarDescription": "Toggle system menu bar visibility",
"messageFormattingTitle": "Message Formatting (Experimental)",
"messageFormattingDescription": "Render messages using Markdown formatting",
"startConversation": "Start New Conversation",
"invalidNumberError": "Invalid Session ID or ONS Name",
"failedResolveOns": "Failed to resolve ONS name",
Expand Down
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@
"linkify-it": "3.0.2",
"lodash": "^4.17.20",
"long": "^4.0.0",
"markdown-it": "^13.0.1",
"markdown-it-abbr": "^1.0.4",
"markdown-it-container": "^3.0.0",
"markdown-it-footnote": "^3.0.3",
"markdown-it-highlightjs": "^4.0.1",
"markdown-it-ins": "^3.0.1",
"markdown-it-mark": "^3.0.1",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"mic-recorder-to-mp3": "^2.2.2",
"minimist": "^1.2.6",
"moment": "^2.29.4",
Expand Down
24 changes: 24 additions & 0 deletions stylesheets/_session.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,31 @@ input {
}

.module-message__text {

white-space: pre-wrap;

p {
display: inline-block;
margin-top: 0.4em;
margin-bottom: 0.25em;
}

ul, ol, blockquote {
margin-bottom: -0.5em;
white-space: normal;

p {
display: block !important;
}
}

pre {
margin-bottom: -0.5em;
}

* {
user-select: text;
}
}

.speedButton {
Expand Down
1 change: 1 addition & 0 deletions stylesheets/manifest.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import '../node_modules/sanitize.css/sanitize.css';
@import '../node_modules/sanitize.css/forms.css';
@import '../node_modules/sanitize.css/typography.css';
@import '../node_modules/highlight.js/styles/stackoverflow-dark.css';

// Global Settings, Variables, and Mixins
@import 'session_constants';
Expand Down
47 changes: 44 additions & 3 deletions ts/components/conversation/message/message-content/MessageBody.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import LinkifyIt from 'linkify-it';

import MarkdownIt from 'markdown-it';
import { RenderTextCallbackType } from '../../../../types/Util';
import { getEmojiSizeClass, SizeClassType } from '../../../../util/emoji';
import { AddMentions } from '../../AddMentions';
Expand All @@ -12,6 +12,42 @@ import { showLinkVisitWarningDialog } from '../../../dialog/SessionConfirm';

const linkify = LinkifyIt();

const markdown = MarkdownIt('default', {
html: false,
linkify: true,
typographer: true,
// This seems not to work:
breaks: false
}
)
// tslint:disable:no-var-requires no-require-imports
.use(require('markdown-it-abbr'))
.use(require('markdown-it-sub'))
.use(require('markdown-it-sup'))
.use(require('markdown-it-ins'))
.use(require('markdown-it-mark'))
.use(require('markdown-it-container'), 'spoiler', {
validate: (params: string) => {
return params.trim().match(/^spoiler\s+(.*)$/);
},

render: (tokens: Array<any>, idx: number) => {
const m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);

if (tokens[idx].nesting === 1) {
// opening tag
return `<details><summary>${markdown.utils.escapeHtml(m[1])}</summary>\n`;
} else {
// closing tag
return '</details>\n';
}
}
})
.use(require('markdown-it-footnote'))
.use(require('markdown-it-highlightjs'), { inline: true }
// tslint:enable:no-var-requires no-require-imports
);

type Props = {
text: string;
/** If set, all emoji will be the same size. Otherwise, just one emoji will be large. */
Expand Down Expand Up @@ -104,8 +140,13 @@ export const MessageBody = (props: Props) => {
);
}

if (text && text.startsWith('```') && text.endsWith('```') && text.length > 6) {
return <pre className="text-selectable">{text.substring(4, text.length - 3)}</pre>;
if (window.getSettingValue('message-formatting')) {
/* tslint:disable:react-no-dangerous-html */
return (
<div className="text-selectable"
dangerouslySetInnerHTML={{__html: `<span style="font-size: 1.1em;">${markdown.render(text).trim()}</span>`}}
/>
);
}

return JsxSelectable(
Expand Down
14 changes: 14 additions & 0 deletions ts/components/settings/section/CategoryAppearance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export const SettingsCategoryAppearance = (props: { hasPassword: boolean | null
? true
: window.getSettingValue(SettingsKey.settingsMenuBar);

const isMessageFormattingActive =
window.getSettingValue(SettingsKey.settingsMessageFormatting) === undefined
? false
: window.getSettingValue(SettingsKey.settingsMessageFormatting);

const isSpellCheckActive =
window.getSettingValue(SettingsKey.settingsSpellCheck) === undefined
? true
Expand All @@ -78,6 +83,15 @@ export const SettingsCategoryAppearance = (props: { hasPassword: boolean | null
active={isHideMenuBarActive}
/>
)}
<SessionToggleWithDescription
onClickToggle={() => {
window.toggleMessageFormatting();
forceUpdate();
}}
title={window.i18n('messageFormattingTitle')}
description={window.i18n('messageFormattingDescription')}
active={isMessageFormattingActive}
/>
<SessionToggleWithDescription
onClickToggle={() => {
window.toggleSpellCheck();
Expand Down
3 changes: 3 additions & 0 deletions ts/data/settings-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const settingsLinkPreview = 'link-preview-setting';
const settingsStartInTray = 'start-in-tray-setting';
const settingsOpengroupPruning = 'prune-setting';

const settingsMessageFormatting = 'message-formatting';

export const SettingsKey = {
settingsReadReceipt,
settingsTypingIndicator,
Expand All @@ -17,4 +19,5 @@ export const SettingsKey = {
settingsLinkPreview,
settingsStartInTray,
settingsOpengroupPruning,
settingsMessageFormatting
};
11 changes: 11 additions & 0 deletions ts/mains/main_renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ Storage.onready(async () => {
window.setMenuBarVisibility(!value);
},

getMessageFormatting: () => Storage.get('message-formatting', true),
setMessageFormatting: async (value: boolean) => {
await Storage.put('message-formatting', value);
},

getSpellCheck: () => Storage.get('spell-check', true),
setSpellCheck: async (value: boolean) => {
await Storage.put('spell-check', value);
Expand Down Expand Up @@ -303,6 +308,12 @@ async function start() {
window.Events.setHideMenuBar(!current);
};

window.toggleMessageFormatting = () => {
const currentValue = window.getSettingValue('message-formatting');
const newValue = currentValue !== undefined ? !currentValue : true;
window.Events.setMessageFormatting(newValue);
};

window.toggleSpellCheck = () => {
const currentValue = window.getSettingValue('spell-check');
// if undefined, it means 'default' so true. but we have to toggle it, so false
Expand Down
1 change: 1 addition & 0 deletions ts/markdown-it.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'markdown-it';
2 changes: 2 additions & 0 deletions ts/types/LocalizerKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type LocalizerKeys =
| 'fileSizeWarning'
| 'openGroupURL'
| 'hideMenuBarDescription'
| 'messageFormattingDescription'
| 'pickClosedGroupMember'
| 'ByUsingThisService...'
| 'startConversation'
Expand Down Expand Up @@ -255,6 +256,7 @@ export type LocalizerKeys =
| 'editMenuDeleteContact'
| 'hideMenuBarTitle'
| 'reactionPopupOne'
| 'messageFormattingTitle'
| 'imageCaptionIconAlt'
| 'sendRecoveryPhraseTitle'
| 'multipleJoinedTheGroup'
Expand Down
3 changes: 3 additions & 0 deletions ts/util/accountManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ async function createAccount(identityKeyPair: SessionKeyPair) {
await Storage.put(SettingsKey.settingsOpengroupPruning, true);
await window.setOpengroupPruning(true);

// Disable message formatting by default.
await Storage.put(SettingsKey.settingsMessageFormatting, false);

await setLocalPubKey(pubKeyString);
}

Expand Down
1 change: 1 addition & 0 deletions ts/window.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ declare global {
toggleCallMediaPermissionsTo: (enabled: boolean) => Promise<void>;
getCallMediaPermissions: () => boolean;
toggleMenuBar: () => void;
toggleMessageFormatting: () => void;
toggleSpellCheck: any;
setTheme: (newTheme: string) => any;
isDev?: () => boolean;
Expand Down
74 changes: 72 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3877,7 +3877,7 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==

entities@^3.0.1:
entities@^3.0.1, entities@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
Expand Down Expand Up @@ -5154,6 +5154,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==

highlight.js@^11.5.1:
version "11.6.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.6.0.tgz#a50e9da05763f1bb0c1322c8f4f755242cff3f5a"
integrity sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==

hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
Expand Down Expand Up @@ -6047,6 +6052,13 @@ [email protected]:
dependencies:
uc.micro "^1.0.1"

linkify-it@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec"
integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==
dependencies:
uc.micro "^1.0.1"

livereload-js@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c"
Expand Down Expand Up @@ -6231,6 +6243,59 @@ map-cache@^0.2.0:
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=

markdown-it-abbr@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/markdown-it-abbr/-/markdown-it-abbr-1.0.4.tgz#d66b5364521cbb3dd8aa59dadfba2fb6865c8fd8"
integrity sha512-ZeA4Z4SaBbYysZap5iZcxKmlPL6bYA8grqhzJIHB1ikn7njnzaP8uwbtuXc4YXD5LicI4/2Xmc0VwmSiFV04gg==

markdown-it-container@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-3.0.0.tgz#1d19b06040a020f9a827577bb7dbf67aa5de9a5b"
integrity sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==

markdown-it-footnote@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.3.tgz#e0e4c0d67390a4c5f0c75f73be605c7c190ca4d8"
integrity sha512-YZMSuCGVZAjzKMn+xqIco9d1cLGxbELHZ9do/TSYVzraooV8ypsppKNmUJ0fVH5ljkCInQAtFpm8Rb3eXSrt5w==

markdown-it-highlightjs@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/markdown-it-highlightjs/-/markdown-it-highlightjs-4.0.1.tgz#6b8eb6a3b971ed592db1ff160cfa5ce9f2e44232"
integrity sha512-EPXwFEN6P5nqR3G4KjT20r20xbGYKMMA/360hhSYFmeoGXTE6hsLtJAiB/8ID8slVH4CWHHEL7GX0YenyIstVQ==
dependencies:
highlight.js "^11.5.1"

markdown-it-ins@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/markdown-it-ins/-/markdown-it-ins-3.0.1.tgz#c09356b917cf1dbf73add0b275d67ab8c73d4b4d"
integrity sha512-32SSfZqSzqyAmmQ4SHvhxbFqSzPDqsZgMHDwxqPzp+v+t8RsmqsBZRG+RfRQskJko9PfKC2/oxyOs4Yg/CfiRw==

markdown-it-mark@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/markdown-it-mark/-/markdown-it-mark-3.0.1.tgz#51257db58787d78aaf46dc13418d99a9f3f0ebd3"
integrity sha512-HyxjAu6BRsdt6Xcv6TKVQnkz/E70TdGXEFHRYBGLncRE9lBFwDNLVtFojKxjJWgJ+5XxUwLaHXy+2sGBbDn+4A==

markdown-it-sub@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz#375fd6026eae7ddcb012497f6411195ea1e3afe8"
integrity sha512-z2Rm/LzEE1wzwTSDrI+FlPEveAAbgdAdPhdWarq/ZGJrGW/uCQbKAnhoCsE4hAbc3SEym26+W2z/VQB0cQiA9Q==

markdown-it-sup@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz#cb9c9ff91a5255ac08f3fd3d63286e15df0a1fc3"
integrity sha512-E32m0nV9iyhRR7CrhnzL5msqic7rL1juWre6TQNxsnApg7Uf+F97JOKxUijg5YwXz86lZ0mqfOnutoryyNdntQ==

markdown-it@^13.0.1:
version "13.0.1"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430"
integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==
dependencies:
argparse "^2.0.1"
entities "~3.0.1"
linkify-it "^4.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"

matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
Expand All @@ -6252,6 +6317,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==

mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==

mem@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3"
Expand Down Expand Up @@ -8856,7 +8926,7 @@ typescript@^4.6.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==

uc.micro@^1.0.1:
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
Expand Down