mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 06:20:28 +03:00 
			
		
		
		
	Add support for sending spoiler markdown (#267)
* Basic spoiler markdown plugin * Remove console.log statement
This commit is contained in:
		
							parent
							
								
									5f7fa0557f
								
							
						
					
					
						commit
						0e8219b200
					
				
					 4 changed files with 173 additions and 17 deletions
				
			
		
							
								
								
									
										37
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										37
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -23,6 +23,9 @@
 | 
			
		|||
        "matrix-js-sdk": "^15.4.0",
 | 
			
		||||
        "micromark": "^3.0.3",
 | 
			
		||||
        "micromark-extension-gfm": "^1.0.0",
 | 
			
		||||
        "micromark-util-chunked": "^1.0.0",
 | 
			
		||||
        "micromark-util-resolve-all": "^1.0.0",
 | 
			
		||||
        "micromark-util-symbol": "^1.0.1",
 | 
			
		||||
        "prop-types": "^15.8.1",
 | 
			
		||||
        "react": "^17.0.2",
 | 
			
		||||
        "react-autosize-textarea": "^7.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -3003,6 +3006,8 @@
 | 
			
		|||
      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
 | 
			
		||||
      "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "optional": true,
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "fast-deep-equal": "^3.1.1",
 | 
			
		||||
        "json-schema-traverse": "^1.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -3018,7 +3023,9 @@
 | 
			
		|||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "optional": true,
 | 
			
		||||
      "peer": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ajv-keywords": {
 | 
			
		||||
      "version": "3.5.2",
 | 
			
		||||
| 
						 | 
				
			
			@ -9989,9 +9996,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/micromark-util-symbol": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ==",
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==",
 | 
			
		||||
      "funding": [
 | 
			
		||||
        {
 | 
			
		||||
          "type": "GitHub Sponsors",
 | 
			
		||||
| 
						 | 
				
			
			@ -10001,7 +10008,8 @@
 | 
			
		|||
          "type": "OpenCollective",
 | 
			
		||||
          "url": "https://opencollective.com/unified"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
      ],
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/micromark-util-types": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -18336,15 +18344,14 @@
 | 
			
		|||
      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "ajv": "^8.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "requires": {},
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "ajv": {
 | 
			
		||||
          "version": "8.9.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
 | 
			
		||||
          "version": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
 | 
			
		||||
          "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "optional": true,
 | 
			
		||||
          "peer": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "fast-deep-equal": "^3.1.1",
 | 
			
		||||
            "json-schema-traverse": "^1.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -18356,7 +18363,9 @@
 | 
			
		|||
          "version": "1.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
 | 
			
		||||
          "dev": true
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "optional": true,
 | 
			
		||||
          "peer": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -23789,9 +23798,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "micromark-util-symbol": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ=="
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "micromark-util-types": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,9 @@
 | 
			
		|||
    "matrix-js-sdk": "^15.4.0",
 | 
			
		||||
    "micromark": "^3.0.3",
 | 
			
		||||
    "micromark-extension-gfm": "^1.0.0",
 | 
			
		||||
    "micromark-util-chunked": "^1.0.0",
 | 
			
		||||
    "micromark-util-resolve-all": "^1.0.0",
 | 
			
		||||
    "micromark-util-symbol": "^1.0.1",
 | 
			
		||||
    "prop-types": "^15.8.1",
 | 
			
		||||
    "react": "^17.0.2",
 | 
			
		||||
    "react-autosize-textarea": "^7.1.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ import { micromark } from 'micromark';
 | 
			
		|||
import { gfm, gfmHtml } from 'micromark-extension-gfm';
 | 
			
		||||
import encrypt from 'browser-encrypt-attachment';
 | 
			
		||||
import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji';
 | 
			
		||||
import { spoilerExtension, spoilerExtensionHtml } from '../../util/markdown';
 | 
			
		||||
import cons from './cons';
 | 
			
		||||
import settings from './settings';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +85,8 @@ function getVideoThumbnail(video, width, height, mimeType) {
 | 
			
		|||
 | 
			
		||||
function getFormattedBody(markdown) {
 | 
			
		||||
  const result = micromark(markdown, {
 | 
			
		||||
    extensions: [gfm()],
 | 
			
		||||
    htmlExtensions: [gfmHtml],
 | 
			
		||||
    extensions: [gfm(), spoilerExtension()],
 | 
			
		||||
    htmlExtensions: [gfmHtml, spoilerExtensionHtml],
 | 
			
		||||
  });
 | 
			
		||||
  const bodyParts = result.match(/^(<p>)(.*)(<\/p>)$/);
 | 
			
		||||
  if (bodyParts === null) return result;
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +407,7 @@ class RoomsInput extends EventEmitter {
 | 
			
		|||
    // Apply formatting if relevant
 | 
			
		||||
    const formattedBody = formatAndEmojifyText(
 | 
			
		||||
      this.matrixClient.getRoom(roomId),
 | 
			
		||||
      editedBody
 | 
			
		||||
      editedBody,
 | 
			
		||||
    );
 | 
			
		||||
    if (formattedBody !== editedBody) {
 | 
			
		||||
      content.formatted_body = ` * ${formattedBody}`;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										143
									
								
								src/util/markdown.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/util/markdown.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/* eslint-disable no-param-reassign */
 | 
			
		||||
/* eslint-disable no-plusplus */
 | 
			
		||||
/* eslint-disable no-continue */
 | 
			
		||||
 | 
			
		||||
import { codes } from 'micromark-util-symbol/codes';
 | 
			
		||||
import { types } from 'micromark-util-symbol/types';
 | 
			
		||||
import { resolveAll } from 'micromark-util-resolve-all';
 | 
			
		||||
import { splice } from 'micromark-util-chunked';
 | 
			
		||||
 | 
			
		||||
function inlineExtension(marker, len, key) {
 | 
			
		||||
  const keySeq = `${key}Sequence`;
 | 
			
		||||
  const keySeqTmp = `${keySeq}Temporary`;
 | 
			
		||||
 | 
			
		||||
  return () => {
 | 
			
		||||
    function tokenize(effects, ok, nok) {
 | 
			
		||||
      const { previous, events } = this;
 | 
			
		||||
 | 
			
		||||
      let size = 0;
 | 
			
		||||
 | 
			
		||||
      function more(code) {
 | 
			
		||||
        // consume more markers if the maximum length hasn't been reached yet
 | 
			
		||||
        if (code === marker && size < len) {
 | 
			
		||||
          effects.consume(code);
 | 
			
		||||
          size += 1;
 | 
			
		||||
          return more;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // check for minimum length
 | 
			
		||||
        if (size < len) return nok(code);
 | 
			
		||||
 | 
			
		||||
        effects.exit(keySeqTmp);
 | 
			
		||||
        return ok(code);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function start(code) {
 | 
			
		||||
        // ignore code if it's not a marker
 | 
			
		||||
        if (code !== marker) return nok(code);
 | 
			
		||||
 | 
			
		||||
        if (previous === marker
 | 
			
		||||
            && events[events.length - 1][1].type !== types.characterEscape) return nok(code);
 | 
			
		||||
 | 
			
		||||
        effects.enter(keySeqTmp);
 | 
			
		||||
        return more(code);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return start;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function resolve(events, context) {
 | 
			
		||||
      let i = -1;
 | 
			
		||||
 | 
			
		||||
      while (++i < events.length) {
 | 
			
		||||
        if (events[i][0] !== 'enter' || events[i][1].type !== keySeqTmp) continue;
 | 
			
		||||
 | 
			
		||||
        let open = i;
 | 
			
		||||
        while (open--) {
 | 
			
		||||
          if (events[open][0] !== 'exit' || events[open][1].type !== keySeqTmp) continue;
 | 
			
		||||
 | 
			
		||||
          events[i][1].type = keySeq;
 | 
			
		||||
          events[open][1].type = keySeq;
 | 
			
		||||
 | 
			
		||||
          const border = {
 | 
			
		||||
            type: key,
 | 
			
		||||
            start: { ...events[open][1].start },
 | 
			
		||||
            end: { ...events[i][1].end },
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          const text = {
 | 
			
		||||
            type: `${key}Text`,
 | 
			
		||||
            start: { ...events[open][1].end },
 | 
			
		||||
            end: { ...events[i][1].start },
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          const nextEvents = [
 | 
			
		||||
            ['enter', border, context],
 | 
			
		||||
            ['enter', events[open][1], context],
 | 
			
		||||
            ['exit', events[open][1], context],
 | 
			
		||||
            ['enter', text, context],
 | 
			
		||||
          ];
 | 
			
		||||
 | 
			
		||||
          splice(
 | 
			
		||||
            nextEvents,
 | 
			
		||||
            nextEvents.length,
 | 
			
		||||
            0,
 | 
			
		||||
            resolveAll(
 | 
			
		||||
              context.parser.constructs.insideSpan.null,
 | 
			
		||||
              events.slice(open + 1, i),
 | 
			
		||||
              context,
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          splice(nextEvents, nextEvents.length, 0, [
 | 
			
		||||
            ['exit', text, context],
 | 
			
		||||
            ['enter', events[i][1], context],
 | 
			
		||||
            ['exit', events[i][1], context],
 | 
			
		||||
            ['exit', border, context],
 | 
			
		||||
          ]);
 | 
			
		||||
 | 
			
		||||
          splice(events, open - 1, i - open + 3, nextEvents);
 | 
			
		||||
 | 
			
		||||
          i = open + nextEvents.length - 2;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      events.forEach((event) => {
 | 
			
		||||
        if (event[1].type === keySeqTmp) {
 | 
			
		||||
          event[1].type = types.data;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return events;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tokenizer = {
 | 
			
		||||
      tokenize,
 | 
			
		||||
      resolveAll: resolve,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      text: { [marker]: tokenizer },
 | 
			
		||||
      insideSpan: { null: [tokenizer] },
 | 
			
		||||
      attentionMarkers: { null: [marker] },
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const spoilerExtension = inlineExtension(codes.verticalBar, 2, 'spoiler');
 | 
			
		||||
 | 
			
		||||
const spoilerExtensionHtml = {
 | 
			
		||||
  enter: {
 | 
			
		||||
    spoiler() {
 | 
			
		||||
      this.tag('<span data-mx-spoiler>');
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  exit: {
 | 
			
		||||
    spoiler() {
 | 
			
		||||
      this.tag('</span>');
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { inlineExtension, spoilerExtension, spoilerExtensionHtml };
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue